Reputation: 351
I have a .net 4 web api with many controllers that all inherit from the same base controller. This base controller has a method that gets the database connection string and uses it to instantiate a new instance of DbContext. The DbContext is then passed to 10+ repositories which all use that instance of DBContext.
The base controller is doing the following:
public IRepository Repository { get; set; }
public BaseController(IRepository repository)
{
this.Repository = repository;
}
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
this.Repository = IRepositoryFactory.GetRepository();
}
public static class IRepositoryFactory
{
public static IRepository GetRepository()
{
// **gets database connectionString**
var context = new DbContext(connectionString)
// this passes context to all the 10+ repositories using same instance
return new IDataRepository(context);
}
}
Is having the base controller instantiate a new instance of DbContext on every api call causing the DbContext to never be reused between calls. In .net core you use the startup class to tell DbContext which database to use, but I don't believe you can do that in .net 4. I am afraid that these new instances of DbContext aren't being used like a Singleton and thus there is no caching going on, causing a lot of unnecessary database hits. Is there a better way to tell DbContext it's connection string and use it without created new instances of DbContext in the BaseController which gets hit on every controller?
Upvotes: 0
Views: 1210
Reputation: 34663
For web applications you should leverage a IoC container to manage dependencies and lifetime scoping. Most of these can be wired directly into MVC to instantiate controllers, resolve references to dependencies such as Repositories, and their dependencies including the DbContext or wrappers like Unit of Work implementations. By default the IoC container should be scoping dependency lifecycles to the Request, so if you have 3 repositories spun up to serve a controller call, they would all receive the same reference to the DbContext to service a request.
If you haven't worked with an IoC container / Dependency Injection, I would recommend having a look at Autofac or Unity. Other common, though older ones include Castle Windsor & Ninject. ASP.Net Core has it's own DI extensions as well. I personally recommend Autofac as it is mature, but fairly young so it's designed around modern paradigms such as Generics and Fluent structure. It also has quite good documentation.
DbContext's should be short-lived, but if you are using several repositories, sharing a single instance for all repository calls is almost always advisable so that entity references can be shared between the repositories, and changes are saved as part of one operation. (Committing or rolling back all for one or none at all.) Separate DbContext instances require complicated code to detach and attach entities to share entities between repositories, and explicit transaction handling to guard operations. Personally I use the repository pattern as a separation for testing in isolation, though I avoid the generic repository pattern. I utilize Mehdime's DbContextScope patterns for Unit of Work which gives me more control over the unit of work as opposed to using the Dependency Injection to manage the lifetime of the DbContext.
Singleton instances of DbContexts for web applications, or any application should be avoided. Firstly, EF does not synchronize data with the database. Any cached entities can easily become stale so the longer a context is alive, the more stale overwrites or exceptions for row version checks (if you have 'em) will be occurring. The caching/tracking features of EF also result in increased memory usage as well as performance degradation as EF wants to check caches for object references and related dependencies with every query. This can lead to some odd behaviour when lazy loading is disabled where EF will populate references that happen to be cached which might not reflect complete sets of data.
Upvotes: 1