0
votes

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'.

2
How IDatabaseFactory<C> is defined? - Dennis
You use DataContext only for get DbSet? - Vlad
@Vlad Yes. I was thinking of removing IDatabaseFactory completely. Maybe I can pass IUnitOfWork in the constructor instead. The context is in the UnitOfWork class. - Ivan-Mark Debono

2 Answers

0
votes

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.

0
votes

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>();
    }
 }