简体   繁体   中英

How to ensure that Autofac is calling Dispose() on EF6 DbContext

UPDATE

Found this little gem which helped me with DbContext Josh Kodroff - Making Entity Framework More Unit-Testable

Original

After doing a lot of research I finally decided to implement IOC using Autofac in my MVC5 EF6 project. Autofac's documentation has been helpful, but I'm still not sure about whether or not I need to call Dispose() either in my Controller or Service Class?

I'm not using an abstracted UOW and Generic Repository, but just relying on DbContext and DbSet<> provided in EF6. Here's a snippet of my classes.

My DbContext

public class ProductContext : DbContext
{
    public ProductContext() : base("ProductContext")
    {
    }

    public DbSet<Availability> Availability { get; set; }       
    public DbSet<Category> Categories { get; set; } 
    ....
 }

My Service Class

    public class ProductService : IProductService
{
    private ProductContext _db;
    public ProductService(ProductContext db)
    {
        _db = db;
    }

    public List<Product> GetProductsByCategory(string cleanCategory)
    {
        return _db.Products
             .Include(p => p.Options.Select(o => o.OptionGroup))
                 .Include(p => p.Associations.Select(a => a.AssociatedGroup))
                 .Include(p => p.Variations).Include(p => p.Manufacturer)
                 .Where(p => p.Active && p.Category.Name.ToUpper().Equals(cleanCategory)).ToList();
    }
    .....
}

My Service Interface

    public interface IProductService
{
    List<Product> GetProductsByCategory(string cleanCategory);
    ....
}

My Contoller

    public class ProductsController : Controller
{
    private IProductService _productService;
    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    //GET: Products/
    public ActionResult Index(string category)
    {
        if (String.IsNullOrEmpty(category))
        {
            return HttpNotFound();
        }

        string cleanCategory = urlScrubber(category);
        var viewModel = new ProductsVM();
        viewModel.ProductList = _productService.GetProductsByCategory(cleanCategory);
}

My Autofac Container

var builder = new ContainerBuilder();

        // Register your MVC controllers.
        builder.RegisterControllers(typeof(MvcApplication).Assembly);

        // REGISTER COMPONENTS HERE:
        builder.RegisterType<ProductContext>().AsSelf().InstancePerRequest();
        builder.RegisterType<ProductService>().As<IProductService>().InstancePerRequest();

        // Set the dependency resolver to be Autofac.
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

I have removed Dispose() from the controller with the understanding that Autofac would handle the disposal of contexts that inherit from IDisposable. Since ProductContext inherits from DbContext which includes a Dispose() Method, this should work.

Do I need to include something like

builder.RegisterType<ProductContext>().As<DbContext>().InstancePerRequest();

or will my current container work as expected calling Dispose?

builder.RegisterType<ProductContext>().AsSelf().InstancePerRequest();

Thanks for any help, I'm having a hard time locating documentation using Autofac without a generic repository and UOW on top of DbContext similar to my current pattern.

As per the doucmentation,

Autofac integration libraries standard unit-of-work lifetime scopes will be created and disposed for you automatically. Autofac's ASP.NET MVC integration , a lifetime scope will be created for you at the beginning of a web request and all components will generally be resolved from there. At the end of the web request, the scope will automatically be disposed - no additional scope creation is required on your part.

So I think if your class implments IDisposable then Dispose() would be automatically called for such objects. So simply,

builder.RegisterType<ProductContext>().As<DbContext>().InstancePerRequest();

Would do the Disposal via object life scope management.

Autofac also supports using Func<> in constructor injection. For example, you can register your data context like normal:

builder.RegisterType<ProductContext>().As<IProductContext>();

and use it as follows in your ProductService :

public class ProductService : IProductService
{
    private IProductContext _dbCreator;
    public ProductService(Func<IProductContext> dbCreator)
    {
        _db = db;
    }

    public List<Product> GetProductsByCategory(string cleanCategory)
    {
        using (var dbCtx = _dbCreator())
        {

            return dbCtx.Products
                 .Include(p => p.Options.Select(o => o.OptionGroup))
                     .Include(p => p.Associations.Select(a => a.AssociatedGroup))
                     .Include(p => p.Variations).Include(p => p.Manufacturer)
                     .Where(p => p.Active && p.Category.Name.ToUpper().Equals(cleanCategory)).ToList();
        }
    }
    .....
}

Basically, your ProductService now has access to a Func<> ( _dbCreator ) that creates a new instance of your ProductContext based on your autofac registration every time it's called, allowing you to dispose the instance when you deem appropriate.

I realized after I wrote this that you don't have an IProductContext , I would usually recommend using this pattern, however, it isn't too important as far as your question is concerned. You can continue to use your current registration method for ProductContext and then just pass in a Func<ProductContext> instead of an IProductContext , ie,

builder.RegisterType<ProductContext>().AsSelf();

and

private ProductContext _dbCreator;
public ProductService(Func<ProductContext> dbCreator)

Sorry if the code doesn't compile, I didn't use an IDE... Hopefully it's close enough for you to get my point!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM