Managing the Entity Framework ObjectContext instance lifetime in WCF and sharing it among repositories


As I pointed out in a previous post about using the Repository pattern in EF and WCF, it’s a bad practice when service operations manage the ObjectContext instance lifetime and have dependencies on it.

In this post I’m going to show you how to share the same ObjectContext among repositories as long as the service is activated (either PerCall or PerSession) and have the service totally agnostic of it.

First lets introduce two concepts:

public interface IDbContext : IDisposable
{
    object Context { get; }
}

public interface IDbContextManager
{
    IDbContext GetDbContext();
}

The IDbContext interface can be used as a wrapper for the ObjectContext or even NHibernate’s ISession. The IDbContextManager will be used by the repositories to get an IDbContext instance.

To simplify things we can extend our existing ObjectContext class using a partial class that implements the IDbContext interface. In this case, since I’m using the same example as the previous post it should look like this:

public partial class MFEntitiesContainer : IDbContext
{
    public object Context
    {
        get { return this; }
    }
}


Lets see how the refactored EntityRepository class looks like:

public class EntityRepository<TEntity> : IRepository<TEntity>
    where TEntity : EntityObject, new()
{
    public EntityRepository(IDbContextManager manager)
    {
        this.SetContext(manager.GetDbContext());
    }

    private void SetContext(IDbContext ctx)
    {
        this.ObjectContext = ctx.Context as ObjectContext;
        this.FetchEntitySetName();
    }

    //.. same implementation as the previous post
}

So now we can use our repositories without having to worry how to give them an ObjectContext anymore. We simply have to supply an implementation of IDbContextManager. In this case, I’m going to implement a DbContextManager that will return the same IDbContext instance while the wcf service is activated in order to reuse it among repositories.

The idea here is to leverage the extensible object pattern implemented in wcf by providing our own extension object to the service’s InstanceContext. If you don’t know what this is I recommend reading this blog post for some background.

The first thing to do is to create the extension class. It will be used to “attach” an IDbContext instance to a wcf service’s InstanceContext.

public sealed class DbContextInstanceExtension : IExtension<InstanceContext>
{
    public DbContextInstanceExtension() { }
    public void Attach(InstanceContext owner){ }
    public void Detach(InstanceContext owner){ }

    public IDbContext Context { get; set; }
}


To add a DbContextInstanceExtension to a WCF service we need to provide an IInstanceContextInitializer object that will participate in the initialization of the service.

public sealed class DbContextInitializer : IInstanceContextInitializer
{
    public void Initialize(InstanceContext instanceContext, Message message)
    {
        instanceContext.Extensions.Add(new DbContextInstanceExtension());
    }
}


To apply this IInstanceContextInitializer to the service we need a behavior. In this case, we want to use an IServiceBehavior. I’m calling it DbContextBehavior and I will also expose it as an attribute to simplify its usage.

public sealed class DbContextBehavior : Attribute, IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (var channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            var channelDispatcher = channelDispatcherBase as ChannelDispatcher;
            if (channelDispatcher != null)
            {
                foreach (var endpointDispatcher in channelDispatcher.Endpoints)
                {
                    endpointDispatcher.DispatchRuntime
                                      .InstanceContextInitializers.Add(new DbContextInitializer());
                }
            }
        }
    }
    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {

    }
    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {

    }
}

Having this I can now implement a proper IDbContextManager object that will create and store an IDbContext instance in the DbContextInstanceExtension.

public sealed class WcfDbContextManager<TContext> : IDbContextManager
    where TContext : IDbContext, new()
{
    public IDbContext GetDbContext()
    {
        return this.GetOrCreateDbContext();
    }

    private IDbContext GetOrCreateDbContext()
    {
        var ctxInstanceExtension = OperationContext.Current
                                                   .InstanceContext
                                                   .Extensions
                                                   .Find<DbContextInstanceExtension>();
        if (ctxInstanceExtension == null)
        {
            //the service has no DbContextInstanceExtensions, so a new TContext is returned
            return new TContext();
        }

        if (ctxInstanceExtension.Context == null)
            ctxInstanceExtension.Context = new TContext();

        return ctxInstanceExtension.Context;
    }
}

This implementation works fine with EntityFramework because the generic TContext type represents an ObjectContext class and you can instantiate it with the new operator. For NHibernate we would have to tweak this class or even create a different one..I might write another post on how we can support both ORM’s here and still make it transparent to our service.

Now we can refactor the OrderService to leverage the DbContextBehavior we just created. Note that we don’t have any reference to the ObjectContext anymore. We simply care about using the repositories and if we had a CustomerRepository for example we could share the same ObjectContext among the repositories.

[DbContextBehavior]
public class OrderService : IOrderService
{
    private readonly IOrderRepository orderRepository;

    public OrderService()
    {
        this.orderRepository = IoC.Current.Container.Resolve<IOrderRepository>();
    }

    //** will be addressed in a different post **
    //public OrderService(IOrderRepository repository)
    //{
    //    this.orderRepository = repository;
    //}

    public IEnumerable<Order> ListOrders()
    {
        return orderRepository.Query();
    }
    public IEnumerable<Order> ListOrdersByCustomer(int customerId)
    {
        return orderRepository.QueryByCustomer(customerId);
    }
}

The IoC class is a singleton class I use that exposes an Inversion of Control container like Unity for example. Note that the commented constructor would be a recommended approach if we could have our service be resolved by the IoC container instead of the default way.

The following code is the what we need to wire up all the types that the IoC container needs to resolve:

var container = IoC.Current.Container;
container.RegisterType(typeof(IRepository<>), typeof(EntityRepository<>)); //generic repository
container.RegisterType<IOrderRepository, OrderRepository>(); //specific repository
container.RegisterType<IDbContextManager, WcfDbContextManager<MFEntitiesContainer>>();

I hope this post helps someone out there trying to figure out how easily we can refactor a wcf service and take benefits from a decoupled architecture.

In my next post I will show how we can use the IoC container to resolve our services.

20 thoughts on “Managing the Entity Framework ObjectContext instance lifetime in WCF and sharing it among repositories

  1. Hello Ed,

    In WCF RIA Services if your DomainService class derives from LinqToEntitiesDomainService you will have only one instance of your ObjectContext class available in your service, thus you can pass it directly to repositories. I think you don’t need to use this technique with RIA Services.

    I’m working on a project that uses WCF RIA Services, Entity Framework and the Repository pattern as well. Soon I will post some examples which might interest you.

    I will provide the source code for this example as soon as I can.

    I’m thinking in creating a project at Google Code to host the source code of each blog post so everyone could access it with a SVN client (I use Tortoise).

    MF.

  2. Did you write this code using Entity framework v2 in .net 4.0? I haven’t got the source code yet…although I went to the google site and I didn’t see anything out there.

    Please let me know if this is still relevent or if there’s a better/different way to handle seperating the DAL from the BLL. Thanks!

  3. I wrote it with EF v1. As far as managing the ObjectContext lifetime in WCF and sharing it among repositories, this technique should still work, although I haven’t tested.

    What may not work is the implementation I’ve got for the base repository class. I should rewrite it soon, and I apologize for not having done it yet but free-time is something I’ve been lacking lately..

    Anyway, the code is still hosted. Try using this link to see the files:

    http://code.google.com/p/mfelicio-wordpress/source/browse/#svn/trunk/2010/Fev/MF.EFRepository

    You should use an SVN client (I use Tortoise) to download the whole code.

  4. Hello Adam,

    You don’t need to implement the IDisposable interface because the class that implements IDbContext already implements IDisposable. That class is your generated ObjectContext class from Entity Framework.
    What I did was extend it by creating a partial class to abstract its use by implementing the IDbContext interface.

  5. Manuel, I want to have different EFcontexts(EDMX files) in separate projects(libs) but share the same generic repository and Igenericreposistory and contextmanager from a separate project.
    Is this what I should to ?
    container.RegisterType<IDbContextManager,WcfDbContextManager>();

    container.RegisterType<IDbContextManager,WcfDbContextManager>();

  6. Ops my text was truncated,
    container.RegisterType<IDbContextManager, WcfDbContextManager>();
    and
    container.RegisterType<IDbContextManager, WcfDbContextManager>(); is that the way to do it?

  7. I can’t seem to submit what i write properly. this is my question: should I register WcfDbContextManager for each of my different EFcontext and that’s all ?

  8. Hello Per,

    That’s a nice question.

    In fact you should register a WcfDbContextManager for each different ObjectContext class but notice that the interface IDbContextManager knows nothing of the ObjectContext so you can’t use dependency injection properly that way. Also note that the WcfDbContextManager class is generic, thus you cannot use it without specifying the generic argument which is an ObjectContext concrete class.

    What you can do is create an IDbContextManager(TContext) interface that extends from IDbContextManager (just for type safety).

    Then create a specific base repository class (that derives from EntityRepository(TEntity) ) for each of your ObjectContext’s and have its constructor depend on the IDbContextManager where TContext is the specific context for those repositories. Then call the base constructor passing the IDbContextManager(TContext) instance and it will work fine because it derives from IDbContextManager.

    I haven’t tried this but that’s what I can think of at the moment. The only problem I’m seeing is that you cannot have the same service use more than one ObjectContext unless you modify the DbContextInstanceExtension to hold more than one IDbContext. For example, using an IDictionary where the string key is infered from the ObjectContext’s Type (that should work and still be transparent for the service).

    I hope this helps. If not, I can try to sumarize all this info into c# code.

    Edit: Apparently wordpress isn’t letting me write the generic tags in comments so I wrote () instead…

    Best regards,

    Manuel Felício.

    • Hi, Manuel. a bit late now but I realize I haven’t said thank you for answering my post from last year. I had actually missed it. so here it is: Thank You for your blogpost. Keep up the god work.

  9. Hi Manuel,
    I was wondering whether you had time to blog about WCF RIA Services, Entity Framework and the Repository pattern as you indicated in one of the comments here. I have not been able to locate it if you did blog about this aspect, and I would appreciate if you would point me to it.

    Best regards,
    Raymond

  10. Hi Manuel!

    I downloaded the source code from svn, but I have always got an error when called host.Open()

    Service ‘MF.Services.OrderService’ has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.

    Regards
    Richard

    • Hi,

      The solution for your problem is to register a different manager for the IDbContext, since the one I provide depends on WCF’s OperationContext.Current and you were using it in a console app without WCF.

Leave a reply to mfelicio Cancel reply