Reputation: 1825
I have a Generic Repository pattern with UnitOfWork and i am using IoC. Other than the base methods used by the repository, i have some custom methods. Instead of implementing the whole IRepository methods again, i have inherited from the GenericRepository class.
Here is my UnitofWork implementation:
public interface IUnitOfWork<T> : IDisposable where T : DbContext
{
int Save();
T Context { get; }
}
public class UnitOfWork<T> : IUnitOfWork<T> where T : MyContext, new()
{
private readonly T _context;
public UnitOfWork()
{
_context = new T();
}
public UnitOfWork(T Context)
{
_context = Context;
}
public int Save()
{
return _context.SaveChanges();
}
public T Context
{
get
{
return _context;
}
}
public void Dispose()
{
_context.Dispose();
}
}
This my Repository implementation:
public interface IGenericRepository
{
IQueryable<T> All<T>() where T : class;
void Remove<T>(int id)where T : class;
void Remove<T>(T entity) where T : class;
void RemoveRange<T>(IList<T> entities) where T : class;
T Find<T>(int id) where T : class;
void Add<T>(T entity) where T : class;
void AddRange<T>(IList<T> entities) where T : class;
void Update<T>(T entity) where T : class;
int SaveChanges();
}
public class GenericRepository<C> : IGenericRepository where C : MyContext
{
protected readonly C _context;
public GenericRepository(IUnitOfWork<C> unitOfWork)
{
_context = unitOfWork.Context;
}
public int SaveChanges()
{
return _context.SaveChanges();
}
public IQueryable<T> All<T>() where T : class
{
return _context.Set<T>();
}
public void Remove<T>(int id) where T : class
{
T entity = _context.Set<T>().Find(id);
if (entity != null)
{
_context.Set<T>().Remove(entity);
}
}
public void Remove<T>(T entity) where T : class
{
if (entity != null)
{
_context.Set<T>().Remove(entity);
}
}
public void RemoveRange<T>(IList<T> entities) where T : class
{
if (entities.Count > 0)
{
_context.Set<T>().RemoveRange(entities);
}
}
public T Find<T>(int id) where T : class
{
return _context.Set<T>().Find(id);
}
public void Add<T>(T entity) where T : class
{
_context.Set<T>().Add(entity);
}
public void AddRange<T>(IList<T> entities) where T : class
{
_context.Set<T>().AddRange(entities);
}
public void Update<T>(T entity) where T : class
{
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
}
Here is an example of Custom-Repository:
public interface IUserAccountRepository : IGenericRepository
{
UserAccount Find(string email, string password);
bool CheckDuplicate(string email);
}
public class UserAccountRepository<C> : GenericRepository<C> where C : CSharpAssigmentContext, IUserAccountRepository
{
protected readonly C _context;
public UserAccountRepository(IUnitOfWork<C> unitOfWork)
{
_context = unitOfWork.Context;
}
public int SaveChanges()
{
return _context.SaveChanges();
}
/// <summary>
/// Find user by email and password
/// </summary>
public UserAccount Find(string email, string password)
{
return _context.Set<UserAccount>().Where(ua => ua.Email == email && ua.Password == password).FirstOrDefault(null);
}
/// <summary>
/// Check wether user exists or not
/// </summary>
public bool CheckDuplicate(string email)
{
return _context.Set<UserAccount>().Any(ua => ua.Email == email);
}
public IQueryable<T> All<T>() where T : class
{
return _context.Set<T>();
}
public void Remove<T>(int id) where T : class
{
T entity = _context.Set<T>().Find(id);
if (entity != null)
{
_context.Set<T>().Remove(entity);
}
}
public void Remove<T>(T entity) where T : class
{
if (entity != null)
{
_context.Set<T>().Remove(entity);
}
}
public void RemoveRange<T>(IList<T> entities) where T : class
{
if (entities.Count > 0)
{
_context.Set<T>().RemoveRange(entities);
}
}
public T Find<T>(int id) where T : class
{
return _context.Set<T>().Find(id);
}
public void Add<T>(T entity) where T : class
{
_context.Set<T>().Add(entity);
}
public void AddRange<T>(IList<T> entities) where T : class
{
_context.Set<T>().AddRange(entities);
}
public void Update<T>(T entity) where T : class
{
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = System.Data.Entity.EntityState.Modified;
}
Here is my Unity IoC code:
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
//UnitOfWork and GenericRepository
container.RegisterType(typeof(IUnitOfWork<CSharpAssigmentContext>),typeof(UnitOfWork<CSharpAssigmentContext>), new HierarchicalLifetimeManager());
container.RegisterType(typeof(IGenericRepository), typeof(GenericRepository<CSharpAssigmentContext>), new HierarchicalLifetimeManager());
//I keep receiving compile ERROR here
container.RegisterType(typeof(IUserAccountRepository), typeof(UserAccountRepository<CSharpAssigmentContext>), new HierarchicalLifetimeManager());
//Services
container.RegisterType(typeof(IUsersAccountsService), typeof(UsersAccountsService), new TransientLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
}
As mentioned in the code, i keep getting a compile time error for the following code:
container.RegisterType(typeof(IUserAccountRepository), typeof(UserAccountRepository<CSharpAssigmentContext>), new HierarchicalLifetimeManager());
The error is:
The type MyContext can't be used as type parameter C in the generic type or method. There is no implicate reference conversion from MyContext to IMyClassRepository.
How to solve this error? Is my implementation correct for custom repositories?
Upvotes: 0
Views: 1940
Reputation: 2776
As far as I can see, there might be a mistake in your UserAccountRepository
class definition:
public class UserAccountRepository<C> : GenericRepository<C>
where C : CSharpAssigmentContext, IUserAccountRepository
That class definition can be read like that:
Type UserAccountRepository is a generic type with generic argument of type C; UserAccountRepository inherits from generic class GenericRepository with generic argument of type C; type C must inherit from class CSharpAssignmentContext and type C must implement interface IUserAccountRepository.
Removing IUserAccountRepository
from type constraints for generic parameter C
and adding it after GenericRepository after a comma should do the job:
public class UserAccountRepository<C> : GenericRepository<C>, IUserAccountRepository
where C : CSharpAssigmentContext
Class definition now can be read like that: Type UserAccountRepository is a generic type with generic argument of type C; UserAccountRepository inherits from generic class GenericRepository with generic argument of type C; type UserAccountRepository must implement interface IUserAccountRepository. Generic argument type (Type C) must inherit from class CSharpAssignmentContext.
When you inherit a class from generic class/interface together with other interfaces, you have to first specify what types you inherit from or implement, and only after that specify generic type constraints:
public class SomeImplementation<T1, T2> : ISomeInterface<T1>, IAnotherInterface<T2>, IDisposable, ICloneable
where T1 : IAbstraction
where T2 : class
Upvotes: 1
Reputation: 109079
The foundation is wrong. A generic repository should have a generic entity parameter. Your "generic" repository does have methods with a generic parameter T
, but there's no guarantee whatsoever that the same entity is always used. This is what the interface should look like:
public interface IGenericRepository<TEntity> where TEntity : class
{
IQueryable<TEntity> All();
void Remove(int id);
void Remove(TEntity entity);
void RemoveRange(IList<TEntity> entities);
TEntity Find(int id);
void Add(TEntity entity);
void AddRange(IList<TEntity> entities);
void Update(TEntity entity);
int SaveChanges();
}
In fact, once you decide that you want a UoW/Repository layer on top of DbContext/DbSet
, I don't see any reason to do it differently than this standard example. There you see a similar generic repository, besides a UoW that contains several repositories.
Having done that, what you call "UserAccountRepository
", should be a service that contains a UoW, that can be injected by your IoC container:
public interface IUserAccountService // No repository!
{
UserAccount Find(string email, string password);
bool CheckDuplicate(string email);
}
Example implementation:
public class UserAccountService : IUserAccountService
{
private readonly IUnitOfWork<CSharpAssigmentContext> _unitOfWork;
public UserAccountService(IUnitOfWork<CSharpAssigmentContext> unitOfWork)
{
this._unitOfWork = unitOfWork;
}
You see that in this UoW/Repository implementation the context isn't exposed. That's one of the purposes of this abstraction layer.
Upvotes: 1