2

Hi I am using the Ninject.MVC Nuget package with my MVC3 app and I have the current bindings setup for some constructor injection.

    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<IUnitOfWork>().To<ERSUnitOfWork>();
        kernel.Bind<IRepository<Recipe>>().To<GenericRepository<Recipe>>();
    }

My controller example is below:

public class RecipesController : Controller
{
    private readonly IUnitOfWork unitOfWork;
    private readonly ERSDbContext context;
    private readonly IRepository<Recipe> recipeRepository;

    public RecipesController(IUnitOfWork unitOfWork, IRepository<Recipe> recipeRepository)
    {
        this.context = new ERSDbContext();
        this.unitOfWork = unitOfWork;
        this.recipeRepository = recipeRepository;
    }
}

I want to remove the private DBContext property from the controller and pass the new ERSDbContext() to the constructors of ERSUnitOfWork and the GenericRepository as part of the constructor injection that Ninject is performing, but preferably keep the initialising of the ERSDbContext class inside the controller?

Any help on how you do this would be appreciated. Thanks

I am kind of hoping that it doesn't require my NinjectWebCommon class having to create the DbContext, I wanted that to be initialised in the controller.

tereško
  • 58,060
  • 25
  • 98
  • 150
Pricey
  • 5,799
  • 12
  • 60
  • 84
  • 1
    DbContexts are very cheap to create. There is no "initialization". The object is created, but no connections are opened or complex structures built. DbContext uses lazy initialization when the first call is made to it. – Erik Funkenbusch Sep 16 '12 at 23:59
  • Related: http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why. – Steven Sep 17 '12 at 09:26
  • I have seen that related post before and I see the example of Method injection for simple projects like my one.. My problem is knowing how to implement that into my current example. – Pricey Sep 17 '12 at 13:13

3 Answers3

3

This is a matter of how you abstract your dependencies.

Because you want to have control over when you create your DbContext instance, you should have a factory that will create your type-specific DbContext instances, like so:

public interface IDbContextFactory
{
     T CreateDbContext<T>() where T : DbContext;
}

(Note, if your DI framework handles type parameters on the generic interface fairly well, then you could go with the IDbContextFactory interface that was introduced with Entity Framework 5)

If you only ever need to create typed DbContext instances with a default, parameterless constructor, you can define your interface like so:

public interface IDbContextFactory
{
     T CreateDbContext<T>() where T : DbContext, new();
}

And then define an implementation like so:

public class DbContextFactory : IDbContextFactory
{

    #region Implementation of IDbContextFactory

    public T CreateDbContext<T>() where T : DbContext, new()
    {
        // Create a new instance of T and return.
        return new T();
    }

    #endregion
}

If you need to call a different constructor, then you'd remove the new() constraint and have to use reflection calls (or, you can create a lambda expression and cache it based on the type of T) to create the typed DbContext.

From there you associate the IDbContextFactory contract with your implementation and inject the IDbContextFactory implementation into your class, like you would any other interface.

casperOne
  • 73,706
  • 19
  • 184
  • 253
  • I fail to see the benefit of this, other than if you want to unit test, in which case you could just make your DbContext derive from a generic interface (which is more straight forward). The addition of an additional factory doesn't seem to make sense. – Erik Funkenbusch Sep 16 '12 at 23:24
  • @MystereMan It was specifically stated in the last sentence of the question that control of creation was something the OP wanted. Just binding to itself doesn't give the OP that control. If you have longer-running methods in your controller then the life of the `DbContext` is extended by default (unless you dispose of it earlier). If a small part of the method code depends on the `DbContext`, better to create and tear down as quickly as possible. Leaving things like this open for the life of the method/controller are generally not good ideas. – casperOne Sep 16 '12 at 23:52
  • @MystereMan Also note, I'm not advocating that the typed `DbContext` derive from an interface, the point is to have a factory that produces them for you. You might not want to use the default parameterless constructor for the `DbContext`. – casperOne Sep 16 '12 at 23:54
  • Sorry, I missed that part of the question. Still, DbContexts have no upfront initialization and are very cheap to create. There is nothing gained from this. – Erik Funkenbusch Sep 17 '12 at 00:01
  • If I implement your factory interface and get that to return a new DbContext when calling CreateDbContext, how is this then passed to the UoW and repos? will it also mean that more than one DbContext would be created in the controller when using more than 1 repo? – Pricey Sep 17 '12 at 08:28
  • @Pricey No, if you implement this interface, then you simply call `Bind` and bind the factory interface to your implementation. The factory doesn't hold onto `DbContext` references, it creates and returns them on-demand. It shouldn't cache at all, just create the new typed `DbContext` and return it. You can then create/dispose as you wish. – casperOne Sep 17 '12 at 11:43
  • Do you have any example of this at all? I'm not sure what you mean :( – Pricey Sep 17 '12 at 12:06
  • 1
    @Pricey Updated the answer with an example. – casperOne Sep 17 '12 at 12:36
  • Thanks for the update, I had the implementation of this created I also noticed that there is also this interface http://msdn.microsoft.com/en-us/library/hh506876%28v=vs.103%29.aspx which is part of EF, not sure whether I should use this considering the difference. My question was more about this line "From there you associate the IDbContextFactory contract with your implementation and inject the IDbContextFactory implementation into your class, like you would any other interface.". I am assuming that means pass this to my UoW and repos and have them call IDbContextFactory.Create()? – Pricey Sep 17 '12 at 13:09
  • 1
    @Pricey You could go with the one defined in the framework, but you might have problems wiring up the interface, given the generic type parameter. Most DI frameworks will handle this fine. If you need customization based on the type of the `DbContext` level, then the framework interface is appropriate. The one above has the type parameter on the method level, which means you can have a fairly standardized implementation. Choose whichever one works for you. As for where to inject the factory, if your UoW is where you need it, then yes, inject it there. – casperOne Sep 17 '12 at 13:16
  • Ok thanks I'll try that and +1 again for the link to the use of the new() constraint. – Pricey Sep 17 '12 at 13:28
  • I'm still lost and get this error "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects." because I need to be able to use the same DbContext in my UoW and repos within the same HTTP scope. This is probably down to the disposing of the DbContext, but currently only my UoW uses IDisposable and i'm struggling to add it to my GenericRepository class. I'm also not even sure if thats what I should be doing. – Pricey Sep 17 '12 at 14:01
  • @Pricey This is an issue separate from your question. I recommend asking another question. Your error has no context, since we can't actually see what you've done, and editing the question to include that would possibly change the validity of some of these answers. – casperOne Sep 17 '12 at 14:07
1

This is one of the benefits of Dependency Injection, it will automatically resolve all constructor parameters and their dependent constructor parameters. It does all this for you, just just need to define the mappings for the objects.

So in your case, you just do this:

kerel.Bind<ERSDbContext>().ToSelf();

Then you can just add ERSDbContext to your UoW and Repo and you're good.

If you want to unit test things, then you will need to abstract your context somehow, either as casperOne mentions or as I mentioned (where you make your DbContext derive from a generic interface) and then do this instead:

kernel.Bind<IDbContext>().To<ERSDbContext>();

One of the benefits of using DI is that it controls the lifetime of objects. If you defer construction of the DbContext to outside of your DI container, then you must manage its lifetime manually, instead of allowing the DI container to manage it based on a lifetime policy (such as automatically destroying it at the end of a request).

I see no measureable benefit to delaying creating the context. I think you are doing premature optimization.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • The problem I was having is that you can't have more than one DbContext in the scope of the same HTTP request if one of them is performing saves. That is why I created the UoW class. Originally my context was in the UoW and along with my repos, but that was bad design. I have now split them up again and use my repos for any queries and my UoW for any saves, and like you say I had to make sure the UoW handled the disposing of the context. Now I am in the situation where I need to pass the same context to both the UoW and repos. – Pricey Sep 17 '12 at 07:47
  • The error related to this is "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects.", this is something I still get when using your above example, unfortunately. Thanks for explaining more about the DbContext, that helps. – Pricey Sep 17 '12 at 07:49
  • Sorry my example didn't show it but there are times when I use more than one repository in the same controller action, which is what caused my problem. – Pricey Sep 17 '12 at 08:11
  • @Pricey - the concurrency issue you're facing is because you're creating multiple contexts and then trying to use them together. That's easily addressed by simply injecting the same DbContext into all your UoW instances, then inject your UoW into your repository. In Ninject, you do this with the InRequestScope() method, which I see you're already using. – Erik Funkenbusch Sep 17 '12 at 17:33
0

Due to my concurrency issue, I removed the use of the factory for the time being and I am using this approach because I am short for time.

kernel.Bind<ERSDbContext>().ToSelf().InRequestScope();

Then in my UoW and GenericRepository the constructors expect a ERSDbContext type param.

I would like to improve this approach later but for now this works.

Pricey
  • 5,799
  • 12
  • 60
  • 84
  • I don't really see a problem with this, as that's exactly what it's designed for. I think your code smell is more about how your are designing your UoW and Repository. Your Repository should take the UoW as a constructor argument, and your UoW should take the context as a constructor argument. – Erik Funkenbusch Sep 17 '12 at 17:35
  • Out of interest why do you think the repository should take the UoW as a constructor argument? shouldn't they be kept separate and the repository should have no concept of how to commit a transaction? only make queries? What i was thinking about was how to make the DbContext an interface that I can pass.. but what I have currently isn't too bad since Ninject is handling the dependency and also I presume adding it to the HttpContext Items collection to make it request scoped? – Pricey Sep 21 '12 at 11:04
  • No, Ninject does not add it to the HttpContext.Items, but rather hooks into the RequestEnd event and then disposes of everything. As for why the repository should take the UoW depends a great deal on how your façade works. You can still pass the UoW to the Repository and also take a dependency on the UoW in your business object as well. That way you can control Commit. – Erik Funkenbusch Sep 21 '12 at 16:27
  • If Ninject doesn't ultimately put the object (or something containing it) in HttpContext Items, how does it retrieve the same instance when next requested? It needs to put it somewhere that is scoped to the request? – Danny Tuppeny Sep 25 '12 at 12:37