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.