Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 251
SeanG80 Avatar asked Oct 14 '15 19:10

SeanG80


People also ask

Does Autofac call Dispose?

Autofac calls Dispose for all instances of components implementing IDisposable once their parent lifetime scope ends. You don't need to do any additional work here.

How do I dispose of DbContext EF core?

When the controller is being disposed, call dispose on your repository and that should dispose the context. If you are using a service layer and not talking to the repository directly from the controller, then call dispose on the service which will call dispose on repo which will dispose the context.

Is AddDbContext scoped?

The AddDbContext extension method registers DbContext types with a scoped lifetime by default.


2 Answers

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.

like image 103
vendettamit Avatar answered Oct 17 '22 04:10

vendettamit


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, i.e.,

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!

like image 27
crunchy Avatar answered Oct 17 '22 03:10

crunchy