I have a simple generic repository.
In this Repository, I have a generic DBContext and thats means its not specific to my appliktion.
The generic DbContext are still able to access my DBcontexts Entites, such
Context.Set<TEntity>().Add(entity)
Do the method Set() here geting reference to my webapplikationens DBContext, or what is happening ?
Full Code:
public interface IRepository<TEntity> where TEntity : class
{
TEntity Get(int id);
void Add(TEntity entity);
}
...
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public TEntity Get(int id)
{
// Here we are working with a DbContext, not PlutoContext. So we don't have DbSets
// such as Courses or Authors, and we need to use the generic Set() method to access them.
//Returns a non-generic DbSet instance for access to entities of the given type in the context and the underlying store .
return Context.Set<TEntity>().Find(id);
}
public void Add(TEntity entity)
{
Context.Set<TEntity>().Add(entity);
}
}
.................................
public interface IAuthorRepository : IRepository<Author>
{
Author GetAuthorWithCourses(int id);
}
..
public class AuthorRepository : Repository<Author>, IAuthorRepository
{
public AuthorRepository(PlutoContext context) : base(context)
{
}
public Author GetAuthorWithCourses(int id)
{
return PlutoContext.Authors.Include(a => a.Courses).SingleOrDefault(a => a.Id == id);
}
public PlutoContext PlutoContext
{
get { return Context as PlutoContext; }
}
}
..
public interface IUnitOfWork : IDisposable
{
IAuthorRepository Authors { get; }
int Complete();
}
...
public class UnitOfWork : IUnitOfWork
{
private readonly PlutoContext _context;
public UnitOfWork(PlutoContext context)
{
_context = context;
Authors = new AuthorRepository(_context);
}
public IAuthorRepository Authors { get; private set; }
public int Complete()
{
return _context.SaveChanges();
}
public void Dispose()
{
_context.Dispose();
}
}
...
public class Repository<Entity> : IRepository<Entity> where Entity : class
{
protected readonly DbContext Context;
public Repository(DbContext context)
{
Context = context;
}
public void Add(Entity entity)
{
Context.Set<TEntity>().Add(entity);
}
}
The argument DbContext context is just a compile-time type, the actual type of the parameter isn't changed.
Related
I have a generic repository pattern with unit of work that does the Insert transaction. Due to some specifics, I have a BaseController that gets a company type, and based on that company type I created the ModelEntities ( with a default model that all database-contexts apply to) from the assigned connection string. This generally works, but also causes EF not to return a database generated value. And this is something I definitely need. The model is a Database first generated model.
The value is Identity specified and has an incremental value, so that's not the problem.
I've done a similar thing before and I don't see why it doesn't return.
I hope one of you guys have an answer to this.
I have attached all code below.
BaseController.cs
public class BaseController : Controller
{
public UnitOfWork _unitofwork;
public BaseController(ModelType.Company company)
{
LoadInContext(company);
}
public void LoadInContext(ModelType.Company company)
{
string connectionString = string.Empty;
switch (company)
{
case ModelType.Company.AT:
connectionString = ConfigurationManager.ConnectionStrings["Entities"].ConnectionString;
break;
case ModelType.Company.ELB:
connectionString = ConfigurationManager.ConnectionStrings["ELBEntities"].ConnectionString;
break;
case ModelType.Company.NB:
connectionString = ConfigurationManager.ConnectionStrings["NBEntities"].ConnectionString;
break;
default:
throw new Exception("Unhandled company");
}
_unitofwork = new UnitOfWork(new Entities().CreateEntitiesForDB(connectionString));
}
}
Repository.cs
public class Repository<T>
{
internal DbContext dbContext;
public IDbSet<T> dbSet;
public Repository(DbContext context)
{
dbContext = context;
dbSet = context.Set<T>();
}
public virtual void Insert(T entity)
{
dbSet.Add(entity);
dbContext.SaveChanges();
}
public virtual void Delete(object id)
{
T entityToDelete = dbSet.Find(id);
Delete(entityToDelete);
}
public virtual void Delete(T entityToDelete)
{
if (dbContext.Entry(entityToDelete).State == EntityState.Detached)
{
dbSet.Attach(entityToDelete);
}
dbSet.Remove(entityToDelete);
dbContext.SaveChanges();
}
public virtual void Update(T entityToUpdate)
{
dbSet.Attach(entityToUpdate);
dbContext.Entry(entityToUpdate).State = EntityState.Modified;
dbContext.SaveChanges();
}
}
UnitOfWork.cs
public class UnitOfWork
{
private readonly DbContext context;
private bool disposed;
public UnitOfWork(DbContext dbContext)
{
context = dbContext;
}
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Save()
{ }
public void SaveChangesAsync()
{ }
public ExampleRepository ExampleRepository
{
return new ExampleRepository(context);
}
}
Example repository
public class ExampleRepository: Repository<Example>
{
public ExampleRepository(DbContext context) : base(context)
{}
}
Example method
public class ExampleController: BaseController
{
public ExampleController() : base(ModelType.Company.AT)
{}
public void ExampleMethod(Example example)
{
_unitofwork.ExampleRepository.Insert(example);
int id = example.Id
//This does not return the ID.
}
}
In WebApi Core, I pass the context to a service collections via AddDataAccess. I really don't understand why the context is null at this line :
var res = this.Context.Set<TEntity>().AsEnumerable();
Some people will say because there is null in in the parameter in the constructor but to my it's correct. Should be something else.
//In the Stratup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<myConnectionStringContext>
(options => options.UseSqlServer(_config["ConnectionStrings:myConnectionString"]));
services.AddDataAccess<myConnectionStringContext>();
// Add framework services.
services.AddMvc();
}
//In my personnal package
public static class ServiceCollectionExtentions
{
public static IServiceCollection AddDataAccess<TEntityContext>(this IServiceCollection services)
where TEntityContext : DbContext
{
RegisterDataAccess<TEntityContext>(services);
return services;
}
private static void RegisterDataAccess<TEntityContext>(IServiceCollection services)
where TEntityContext : DbContext
{
services.AddTransient(typeof(TEntityContext));
services.AddTransient(typeof(IRepository<>), typeof(GenericEntityRepository<>));
}
}
public class EntityBase
{
[Key]
public int Id { get; set; }
}
public interface IRepository<TEntity>
{
IEnumerable<TEntity> GetAll();
}
public class GenericEntityRepository<TEntity> : EntityRepositoryBase<DbContext, TEntity>
where TEntity : EntityBase, new()
{
public GenericEntityRepository() : base(null)
{ }
}
public abstract class EntityRepositoryBase<TContext, TEntity> : RepositoryBase<TContext>, IRepository<TEntity>
where TContext : DbContext where TEntity : EntityBase, new()
{
protected EntityRepositoryBase(TContext context) : base(context)
{ }
public virtual IEnumerable<TEntity> GetAll()
{
return this.Context.Set<TEntity>().AsEnumerable();
}
}
public abstract class RepositoryBase<TContext> where TContext : DbContext
{
protected TContext Context { get; private set; }
protected RepositoryBase( TContext context)
{
this.Context = context;
}
}
The Problem
Because you are passing a null here:
public GenericEntityRepository() : base(null) // <-- passing null
This goes to the parent EntityRepositoryBase
protected EntityRepositoryBase(TContext context) : base(context) // <--(1) context = null
Then goes to the parent RepositoryBase
protected TContext Context { get; private set; } // <--(4) context = null
protected RepositoryBase(TContext context) // <--(2) context = null
{
this.Context = context; // <--(3) context = null
}
So when you call
return this.Context.Set<TEntity>().AsEnumerable();
this.Context // <-- this is null
The Solution
You need to change your GenericEntityRepository from sending a null to sending the dbContext
public class GenericEntityRepository<TEntity> : EntityRepositoryBase<DbContext, TEntity>
where TEntity : EntityBase
{
public GenericEntityRepository(ApplicationDbContext dbContext) : base(dbContext) { }
}
Alternatively, you can do this too (preferred):
public class GenericEntityRepository<TContext, TEntity> : EntityRepositoryBase<TContext, TEntity>
where TContext: DbContext, new()
where TEntity : EntityBase
{
public GenericEntityRepository() : base(new TContext()) { }
}
Note: With the code you have provided, you could probably do away with a couple of these generic classes, since they serve no function other than just inheriting unnecessarily.
Please help me correct this code.
I am having a compiler error in the class
public interface IGenericSaveRepository
{
void Save<TEntity>(int id, ICollection<TEntity> entities) where TEntity : class;
}
public class GenericSaveRepository<TEntity> where TEntity : class,IGenericSaveRepository
{
private IUnitofWork<TEntity> _unitofWork;
private NaijaSchoolsContext _context;
public GenericSaveRepository(NaijaSchoolsContext context)
{
_context = context;
_unitofWork = new UnitofWork<TEntity>(_context);
}
public void Save(int id, ICollection<TEntity> entities)
{
foreach (var entity1 in entities)
{
//entity.Insert(entity1);
_unitofWork.Entity.Insert(entity1);
}
}
}
public class RatingRepo : GenericRepository<Rating>
{
private IGenericSaveRepository gen;
private readonly NaijaSchoolsContext _context;
public RatingRepo(NaijaSchoolsContext context)
: base(context)
{
_context = context;
}
public void Save(School school,Rating rating)
{
List<Rating> ratings = new List<Rating>();
ratings.Add(rating);
gen = new GenericSaveRepository<Rating>(_context);
gen.Save(23, ratings);
}
}
This line gen = new GenericSaveRepository<Rating>(_context); doesn't allow me to have Rating specified as a concrete type.
How can I do this?
Thanks for the help.
This should remove your compile error .. see gen implementation if you don't want Rating must implement IGenericSaveRepository
public class RatingRepo : GenericRepository<Rating>
{
private GenericSaveRepository<Rating> gen;
private readonly NaijaSchoolsContext _context;
public RatingRepo(NaijaSchoolsContext context)
: base(context)
{
_context = context;
}
public void Save(School school, Rating rating)
{
List<Rating> ratings = new List<Rating>();
ratings.Add(rating);
gen = new GenericSaveRepository<Rating>(_context);
gen.Save(23, ratings);
}
}
Update : Complete implementation
public interface IGenericSaveRepository
{
void Save<TEntity>(int id, ICollection<TEntity> entities) where TEntity : class;
}
public class GenericSaveRepository<TEntity> where TEntity : class
{
private UnitofWork<TEntity> _unitofWork;
private NaijaSchoolsContext _context;
public GenericSaveRepository(NaijaSchoolsContext context)
{
_context = context;
_unitofWork = new UnitofWork<TEntity>(_context);
}
public void Save(int id, ICollection<TEntity> entities)
{
foreach (var entity1 in entities)
{
}
}
}
public class UnitofWork<T>
{
public UnitofWork(NaijaSchoolsContext context)
{
throw new NotImplementedException();
}
}
internal interface IUnitofWork<T>
{
}
public class NaijaSchoolsContext
{
}
public class GenericRepository<T>
{
protected GenericRepository(NaijaSchoolsContext context)
{
throw new NotImplementedException();
}
}
public class Rating
{
}
public class School
{
}
Rating must implement IGenericSaveRepository in order for that to compile.
public class GenericSaveRepository<TEntity> where TEntity : class,IGenericSaveRepository
Say I have the following implementation:
//Generic repository.
public interface IRepository<T> where T : class {
void Insert(T entity);
void Delete(int id);
void SaveChanges();
//..more generic functions
}
//Repository implementation.
public class EFRepository<T> : IRepository<T> where T: class {
private MyDbContext context;
protected DbSet<T> dbSet;
public EFRepository(): this(new MyDbContext()){}
public EFRepository(MyDbContext context)
{
this.context = context;
dbSet = context.Set<T>();
}
public void Insert(T entity)
{
dbSet.Add(entity);
}
public void Delete(int id)
{
dbSet.Remove(dbSet.Find(id));
}
public void SaveChanges()
{
context.SaveChanges();
}
//...more generic implementations
}
//Unit of Work Interface
public interface IUnitOfWork: IDisposable
{
IRepository<EntityA> ARepository { get; }
IRepository<EntityB> BRepository { get; }
//...more stuff
}
//Unit of Work Implementation
public class EFUnitOfWork: IUnitOfWork
{
private MyDbContext context = new MyDbContext();
private IRepository<EntityA> aRepository;
private IRepository<EntityB> bRepository;
public IRepository<EntityA> ARepository
{
get
{
if (this.aRepository == null)
this.aRepository = new EFRepository<EntityA>(context);
return this.aRepository;
}
}
public IRepository<EntityB> BRepository
{
get
{
if (this.bRepository == null)
this.bRepository = new EFRepository<EntityB>(context);
return this.bRepository;
}
}
//...more stuff
}
And finally, I have the following bindings in my resolver:
kernel.Bind(typeof(IRepository<>)).To(typeof(EFRepository<>));
kernel.Bind(typeof(IUnitOfWork)).To(typeof(EFUnitOfWork));
Now, my question is... how would I go about extending the repository for EntityA so that it has more operations than just the generic ones?
I'll post what I have so far in a few...
EDIT: Here's what I have so far:
//New interface.
public class IEntityARepository : IRepository<EntityA>
{
void DoSomethingSpecificToEntityA();
}
//New implementation.
public class EFEntityARepository : EFRepository<EntityA>
{
public EFEntityARepository(MyDbContext context) : base(context) {}
//add additional methods for EntityA
public void DoSomethingSpecificToEntityA()
{
}
}
//Modify Unit of Work Interface as follows.
//Unit of Work Interface
public interface IUnitOfWork: IDisposable
{
IEntityARepository ARepository { get; }
IRepository<EntityB> BRepository { get; }
//...more stuff
}
//Modify Unit of Work Implementation as follows.
public class EFUnitOfWork: IUnitOfWork
{
private MyDbContext context = new MyDbContext();
private IEntityARepository aRepository;
private IRepository<EntityB> bRepository;
public IEntityARepository ARepository
{
get
{
if (this.aRepository == null)
this.aRepository = new EFEntityARepository<EntityA>(context);
return this.aRepository;
}
}
public IRepository<EntityB> BRepository
{
get
{
if (this.bRepository == null)
this.bRepository = new EFRepository<EntityB>(context);
return this.bRepository;
}
}
//...more stuff
}
Add the following binding:
kernel.Bind(typeof(IEntityARepository)).To(typeof(EFEntityARepository));
However, I'm sure this is not correct. Or at least, not the proper way to do it.
If I understand you correctly, you could just derrive from a specifically typed version of your generic class like this...
public class EFEntityARepository : EFRepository<EntityA>, IEntityARepository
{
//Add more opps
}
I think the unit of work needs to look like this:
public IEntityARepository ARepository
{
get
{
if (this.aRepository == null)
this.aRepository = new EFEntityARepository(context);
return this.aRepository;
}
}
OK so I've got it working by adding and/or modifying my original code as follows:
//New interface for the extension of the repository.
//Is it possible to do this without defining this new interface? Doesn't seem like it.
public class IEntityARepository : IRepository<EntityA>
{
void DoSomethingSpecificToEntityA();
}
//Add new class.
//It looks like you have to inherit from IEntityARepository as well.
public class EFEntityARepository : EFRepository<EntityA>, IEntityARepository
{
public EFEntityARepository(MyDbContext context) : base(context) {}
//add additional methods for EntityA
public void DoSomethingSpecificToEntityA()
{
}
}
//Modify Unit of Work Interface as follows.
public interface IUnitOfWork: IDisposable
{
IEntityARepository ARepository { get; }
IRepository<EntityB> BRepository { get; }
//...more stuff
}
//Modify Unit of Work Implementation as follows.
public class EFUnitOfWork: IUnitOfWork
{
private MyDbContext context = new MyDbContext();
private IEntityARepository aRepository;
private IRepository<EntityB> bRepository;
public IEntityARepository ARepository
{
get
{
if (this.aRepository == null)
this.aRepository = new EFEntityARepository(context);
return this.aRepository;
}
}
public IRepository<EntityB> BRepository
{
get
{
if (this.bRepository == null)
this.bRepository = new EFRepository<EntityB>(context);
return this.bRepository;
}
}
//...more stuff
}
It works... but is it the best way to go about it?
I am playing around with making a nhibernate generic repo but I am unsure how to make a method to return an entity back by ID
public class NhibernateRepo : INhibernateRepo
{
private readonly ISession session;
public NhibernateRepo(ISession session)
{
this.session = session;
}
public void Create<T>(T entity)
{
session.Save(entity);
}
public void CreateOrUpdate<T>(T entity)
{
session.SaveOrUpdate(entity);
}
public void Delete<T>(T entity)
{
session.Delete(entity);
}
public void Update<T>(T entity)
{
session.Update(entity);
}
public T Get<T>(object id)
{
return session.Get<T>(id);
}
public T Load<T>(object id)
{
return session.Load<T>(id);
}
public void ReadOnly(object entity, bool readOnly = true)
{
session.SetReadOnly(entity, readOnly);
}
public void Evict(object entity)
{
session.Evict(entity);
}
public object Merge(object entity)
{
return session.Merge(entity);
}
public IEnumerable<T> FindAll<T>()
{
return session.Query<T>();
}
}
You can define this method in the repository interface:
interface IRepository<TEntity, TId> where TEntity : class {
TEntity FindById(TId id);
...
}
And following in repository implementation:
class Repository<TEntity, TId> : IRepository<TEntity, TId> where TEntity : class{
public TEntity FindById(TId id) {
return _session.Get<TEntity>(id);
}
}
As a side note, generic repository interface is sometimes considered to be a bad practice from Domain Driven Design perspective.