简体   繁体   中英

Generic UnitOfWork and Repository

I'm refactoring my code and I started by removing a reference to Entity Framework in my service layer. This layer uses unit of work and repositories (through interfaces) located in my DAL layer.

Now I encountered a problem because my base repository class looks like this:

public interface IDatabaseFactory<C> : IDisposable
{
    C Get();
    void Set(string connectionString);
}

public abstract class Repository<C, T> : IRepository<T> 
                                         where C : DbContext, IBaseContext 
                                         where T : class, IEntity
{
    protected readonly IDbSet<T> dbset;
    private C dataContext;

    protected Repository(IDatabaseFactory<C> databaseFactory)
    {
        this.DatabaseFactory = databaseFactory;
        this.dbset = DataContext.Set<T>();
    }

    protected IDatabaseFactory<C> DatabaseFactory
    {
        get;
        private set;
    }

    protected C DataContext
    {
        get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
    }

    public virtual void Add(T entity)
    {
        dbset.Add(entity);
    }

    //etc...
}

I obviously need the DbContext constraint on type C. However, if I do so, I get errors on dataContext because it cannot resolve C in DbContext.

How can I overcome this problem?

EDIT

A typical repository looks like this:

public interface ICustomerTypeRepository : IRepository<CustomerType> { }

public class CustomerTypeRepository : Repository<IBaseContext, CustomerType>, ICustomerTypeRepository
{
    public CustomerTypeRepository(IDatabaseFactory<IBaseContext> databaseFactory)
        : base(databaseFactory) { }
}

After the changes suggested below, I still get the same errors:

The type 'IBaseContext' cannot be used as type parameter 'TContext' in the generic type or method 'Repository'. There is no implicit reference conversion from 'IBaseContext' to 'System.Data.Entity.DbContext'.

To fix compilation error, you have to put appropriate constraints to your generic types (I've replaced C to TContext for readability):

interface IBaseContext { }
interface IDatabaseFactory<TContext> : IDisposable
    where TContext : DbContext
{
    TContext Get();
    void Set(string connectionString);
}

interface IEntity { }
interface IRepository<T> 
    where T : class, IEntity
{ }

abstract class Repository<TContext, T> : IRepository<T>
    where TContext : DbContext, IBaseContext
    where T : class, IEntity
{
    protected readonly IDbSet<T> dbset;
    private TContext dataContext;

    protected Repository(IDatabaseFactory<TContext> databaseFactory)
    {
        this.DatabaseFactory = databaseFactory;
        this.dbset = DataContext.Set<T>();
    }

    protected IDatabaseFactory<TContext> DatabaseFactory { get; private set; }
    protected TContext DataContext
    {
        get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
    }

    public virtual void Add(T entity)
    {
        dbset.Add(entity);
    }

    //etc...
}

But from the point of architecture, looks like that holding a reference to DbContext in repository is superfluous. Of course, it is hard to propose better solutiuon without knowledge about all of your types.

For remove DbContext from your code you need change IDatabaseFactory like this:

public interface IDatabaseFactory : IDisposable
{
    T Set<T>() where T : IEntity;
}

Then you can change Repository

public abstract class Repository<T> : IRepository<T> 
                                     where T : class, IEntity
{
    protected readonly IDbSet<T> dbset;

    protected Repository(IDatabaseFactory databaseFactory)
    {
        this.dbset = databaseFactory.Set<T>();
    }
    // other code
}

Implementation of IDatabaseFactory:

public class DatabaseFactory : IDatabaseFactory
{
    private readonly DbContext _context;

    public DatabaseFactory(DbContext context)
    {
        _context = context;
    }

    public T Set<T>() where T : Entity
    {
        return _context.Set<T>();
    }
 }

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