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.
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.
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.
The AddDbContext extension method registers DbContext types with a scoped lifetime by default.
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
, 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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With