Using Entity Framework with Castle Windsor
Asked Answered
G

1

24

I use the Entity Framework database-first approach to generate a DbContext / POCO model for an MVC application. I want to avoid having dependencies on DbContext in my controllers to enable me to switch to another persistence provider as I need to (for example for unit testing purposes).

To do this I want to use the Castle Windsor IoC container. I plan to register DbContext as an IUnitOfWork service and to register a generic IRepository service, implementations of which I will use to access and work with aggregate roots in the model.

I'm new to Windsor and haven't been able to find much info about using it with EF, and I have a couple of questions:

  • is this a reasonable approach if I want to decouple EF from the application?
  • how do I install / register the IUnitOfWork and generic IRepository services?
Gingham answered 27/3, 2013 at 8:32 Comment(7)
To whoever marked this down ... would you like to provide a comment saying why? If you think I have missed the point somewhere, you might help me by explaining why.Gingham
I think it was down voted because this is a discussion question, not really appropriate for StackOverflow, especially the first point. The second point can be found at the Castle Windsor site, they have excellent documentation. Wrt the content of your question: mocking DbContext for unit tests is virtually impossible. I use 'unit test' projects against a database for DAL testing besides pure unit test projects for other BL. Caste Windsor injects contexts from a context factory, not interfaced, and interfaced services, validators etc.Carpogonium
Thanks for your comment @GertArnold. Regarding the mocking issue, I'm not wanting to unit test the DAL, but to be able to provide mock repositories to the application layer (eg controllers), to test it. The Castle documentation is good in general, but deals with NHibernate integration, and is pretty sketchy in that area.Gingham
I can't believe this was marked down. A couple of perfectly interesting questions there.Portulaca
Before you start creating a IUnitOfWork and a generic Repository you might want to read this: ayende.com/blog/3955/repository-is-the-new-singleton. (I hope I don't get flamed for this one). The blog might give you more interesting ideas about unit testing and NHibernate as wellHeathenism
@Marwijn, this is a helpful blog, I have been asking myself if Repository and UnitOfWork are redundant when using EF DbContext for the same reasons: EF essentially implements them. However, if you want to encapsulate query code and be able to mock or swap out persistence providers, the extra layer of abstraction is essential. So in the end I don't agree that Repository is redundant. The case of complex queries can be dealt by passing Specification objects to Repository queries (as someone points out in the blog comments).Gingham
I agree with PaulTaylor and @Portulaca - this is a perfectly good question and applicable for this site. I probably say that because I was "Googled" here with the same question. +1 for the question! Also, I agree that EF encapsulates the UOW capabilities, but for using multiple DBContexts and complex functionality, you will need that extra layer. But, start out YAGNI and move that direction only if you need it.Kain
G
25

So, some conclusions. I thought I'd write this up in case it is of use to someone else trying to use / unit test EF, Windsor and MVC together.

First of all, as DbContext implements both the Repository and Unit of Work patterns, you need to take view on whether these implementations will serve or whether you need to create your own.

I chose to create my own Repository, following the DDD pattern: one per aggregate root. The reasons: to encapsulate query code, to prevent it from leaking into the application layer, and to be able to mock more easily when testing the application controllers. I created a generic repository based on IRepository<TEntity>. There are plenty of examples out there. I found this a good one: http://architects.dzone.com/articles/implementing-repository

On the other hand I decided to drop the IUnitOfWork service, opting for the default implementation instead. However, I created an IDbContext abstraction (don't know why Microsoft didn't do this themselves), so that I could mock the DbContext when testing the Repository services.

I gave IDbContext only the members of DbContext that I wanted to use in the repository. So:

public interface IDbContext: IDisposable
{
    Database Database { get; }
    DbEntityEntry Entry(object entity);
    IDbSet<TEntity> Set<TEntity>() where TEntity : class;
    int SaveChanges();
}

I then created a Windsor facility and installer for my IDbContext and IRepository services:

public class EntityFrameworkFacility: AbstractFacility
{
    protected override void Init()
    {
        Kernel.Register(Component.For<IDbContext>()
                                 .ImplementedBy<MyEntities>()
                                 .LifestylePerWebRequest(),
                        Component.For(typeof(IRepository<>))
                                 .ImplementedBy(typeof(Repository<>))
                                 .LifestylePerWebRequest());
    }
}

public class PersistenceInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.AddFacility<EntityFrameworkFacility>();
    }
}

The final piece was to extend the Entity Framework context class to implement IDbContext, and to shadow the Set() method to return IDbSet rather than DbSet:

public partial class MyEntities : IDbContext
{
    public new IDbSet<TEntity> Set<TEntity>() where TEntity : class
    {
        return base.Set<TEntity>();
    }
}

With all this in place (and the ControllerFactory registration illustrated in the Windsor docs), it becomes trivial to get Windsor to inject IRepository objects (or IDbContext) into controller constructors, as required:

public ControllerBase(IRepository<Contact> repo)
{
    _repo = repo;
}

In the Repository unit tests, a real repository instance can be backed with a mock IDbContext:

mocks = new MockRepository();
context = mocks.StrictMock<IDbContext>();
repo = new Repository<Contact>(context);

In Controller unit tests, a mock repository can be used:

mocks = new MockRepository();
repo = mocks.StrictMock<IRepository<Contact>>();
ContactController controller = new ContactController(repo);
Gingham answered 28/3, 2013 at 20:6 Comment(3)
Thanks for taking the time to provide sample code and sum up :-)Eichler
One minor correction, your MyEntities needs to extend DbContext.Pule
Actually, the Entity Framework generated context class inherits from DbContext - this is a partial class that extends that class to support IDbContext as well.Gingham

© 2022 - 2024 — McMap. All rights reserved.