Matt Rowett
Matt Rowett

Reputation: 266

Abstracting EntityFramework.dll reference using Repository/UoW/DI

I've been researching best practice for a new project when using EF (5.0 Beta Code First) as my ORM/DAL. Key considerations are testability, loosely coupled design and unit of work/transactional support across repositories.

I understand that within EF DbContext is UoW and DbSet is Repository and in a service layer you can make changes across multiple repositories co-ordinated by the DbContexts SaveChanges() method. Here's my sample configuration:

public interface IMyContext
{
    IDbSet<Foo> Foos{ get; }

    IDbSet<Bar> Bars { get; }

    int SaveChanges();

    void Dispose();
}

public class EFMyContext : DbContext, IMyContext
{
    private IDbSet<Foo> _foos;
    private IDbSet<Bar> _bars;

    public EFMyContext()
        : base("name=MyConnectionString")
    {
        Database.SetInitializer<EFMyContext>(null);
    }

    public IDbSet<Foo> Foos
    {
        get
        {
            if (_foos == null)
            {
                _foos = this.Set<Foo>();
            }

            return _foos;
        }
    }

    public IDbSet<Bar> Bars
    {
        get
        {
            if (_bars == null)
            {
                _bars = this.Set<Bar>();
            }

            return _bars;
        }
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new FooConfiguration());
        modelBuilder.Configurations.Add(new BarConfiguration());

        base.OnModelCreating(modelBuilder);
    }

All fine so far. If I want to use this is an upper layer like this

public class MyService : IMyService
{
    private IMyContext _context;

    public MyService(IMyContext context)
    {
        _context = context;
    }

    public void DoSomethingWithMyContext()
    {
        // fancy implementation code
    }
}

Here I run into problems as I have a direct dependency on EntityFramework.dll as MyContext exposes properties of type IDBSet. This doesn't seem right and is very similar to this question.

So my question is, how to abstract away the direct dependency on EF? I thought about introducting my own repository and unit of work to pass around a live context but how would this impact change tracking and all the other neat EF features?

The other thing I'm looking at is an IoC mechanism to manage the lifecycle of the context. Could I do away with the UoW at that point?

Sorry for the vagueness, I'm at research saturation point with this and need a definitive place to start before implementation.

Upvotes: 2

Views: 1336

Answers (3)

Rahul P Nath
Rahul P Nath

Reputation: 354

You could very well not have the DbSet properties on the IMyContext and have the dbsets only on the implementation. IMyContext should only be used to know about saving a context(and disposing it off) and nothing else. So inside the repository you can have a strategy that knows how to talk to sql, say using ef and have that handle the dbsets part of it. You wuld need to do this only if you have really plans of moving out EF and replacing it with something else in future.

You could find more details on an approach for this here

Upvotes: 0

Dmitriy Startsev
Dmitriy Startsev

Reputation: 1462

In the Service Layer you don't really need to use IQueryable<T>.

Because this way you will be tightly coupled with ORM that supports LINQ. All LINQ queries should be encapsulated inside concrete DAL.

Better you can define DAL-agnostic repositories in your Service Layer:

public interface IRepository<T>
{
    T GetById(int id);
    IEnumerable<T> GetAll();

    // Filter is some DTO that will be mapped to 
    // concrete LINQ query inside your DAL
    IEnumerable<T> GetFiltered(Filter<T> filter);

    void Add(T item);
    void Update(T item);
    void Remove(T item);
}

UPDATE: If you in love with LINQ, you can define your own interfaces for ObjectContext and ObjectSet:

public interface IObjectContext : IUnitOfWork
{
    IObjectSet<T> Set<T>() where T : class;
}

public interface IObjectSet<T> : IObjectQuery<T>
    where T : class
{
    void Remove(T entity);
    void Add(T entity);
}

public interface IObjectQuery<out T> : IQueryable<T>, IQueryableWrapper
     where T : class
{
    IObjectQuery<T> Include(string path);
}

Then you can write adapters for EF. You also should add Query Translation layer to your adapters, because EF can't understand your interfaces in expression trees. Here is example of QueryTranslator: https://stackoverflow.com/a/1846030/1275399

Upvotes: 1

Ladislav Mrnka
Ladislav Mrnka

Reputation: 364349

Ask yourselves a question: What should my architecture solve? Unless you can provide bulletproof answer why referencing EntityFramework.dll from your service assembly is wrong you are wasting your time.

There is actually nothing wrong in referencing EntityFramework.dll. Why it should be? Your service doesn't expose anything related to EF so it is fully mockable. If you also want to unit test your service code you will have to make sure that your service operates with EF in abstracted way. In such case repository can be way to go but it will be heavy specialized repository - not a generic nonsense usually proposed. The repository will hide everything related to EF and your service will not know that anything like EF exists in your application. It also answers your question about EF features - your service must be completely independent on those features if you want to have it testable. For example you should not make your service code dependent on LINQ-to-Entities or lazy loading. It just causes troubles in testing.

Even if you implement repositories there is still nothing wrong if they are in the same assembly as your services and that assembly references EntityFramework.dll.

Upvotes: 2

Related Questions