简体   繁体   中英

Generic Repository with UnitOfWork with IoC Custom Methods

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?

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM