Questions about Entity Framework Context Lifetime
Let's get controversial!
I disagree with the general MVC + EF consensus that keeping a context alive throughout the entire request is a good thing for a number of reasons:
Low performance increase
Do you know how expensive creating a new database context is? Well... "A DataContext is lightweight and is not expensive to create" that's from MSDN
Get the IoC wrong and it'll seem fine.. until you go live
If you set up your IoC container to dispose of your context for you and you get it wrong, you really really get it wrong. I've twice now seen massive memory leaks created from an IoC container not always disposing of a context correctly. You won't realise you've set it up wrong until your servers start crumbling during normal levels of concurrent users. It won't happen in development so do some load tests!
Accidental lazy loading
You return an IQueryable of your most recent articles so that you can list them on your homepage. One day someone else is asked to show the number of comments next to the respective article. So they add a simple bit of code to the View to show the comment count like so...
@foreach(var article in Model.Articles) {
<div>
<b>@article.Title</b> <span>@article.Comments.Count() comments</span>
</div>
}
Looks fine, works fine. But actually you didn't include the comments in your returned data so now this will make a new database call for each article in the loop. SELECT N+1 issue. 10 article = 11 database calls. Okay so the code is wrong but it is an easy mistake to make so it will happen.
You can prevent this by shutting your context down in you data layer. But won't the code break with a NullReferenceException on the article.Comments.Count() ? Yes it will so it will force you to edit the Data layer to get the data needed for the View layer. This is how is should be.
Code smell
There is just something wrong about hitting the database from your View. You know that an IQueryable hasn't actually hit the database yet right so forget that object. Make sure your database is hit before it leaves your data layer.
So the answer
Your code should be (in my opinion) like this
DataLayer:
public List<Article> GetArticles()
{
List<Article> model;
using (var context = new MyEntities())
{
//for an example I've assumed your "MyTable" is a table of news articles
model = (from mt in context.Articles
select mt).ToList();
//data in a List<T> so the database has been hit now and data is final
}
return model;
}
Controller:
public ActionResult Index()
{
var model = new HomeViewModel(); //class with the bits needed for you view
model.Articles = _dataservice.GetArticles(); //irrelevant how _dataService was intialised
return View(model);
}
Once you have done this and understand this then perhaps you can begin experimenting with having an IoC container handle context but definitely not before. Head my warning - I've seen two large scale failures :)
But honestly do what you like, programming is fun and should be a matter of preference. I'm just telling you mine. But whatever you do, don't start using IoC context per controller or per request just because "all the cool kids are doing it." Do it because you really truly care about it's benefits and understand how it's done correctly.
Entity Framework Context Lifetime in ASP.NET Core Blazor Server
In Blazor Server, scoped service registrations can be problematic because the instance is shared across components within the user's circuit. DbContext isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate. For more info see ASP.NET Core Blazor Server with Entity Framework Core.
You should create a new DbContext instance. One way to create a new DbContext instance is using using
statement.
Try this code:
[Inject] IDbContextFactory<DBContext> DbFactory;
private string updateMessage(int messageID)
{
using (DBContext _context = DbFactory.CreateDbContext())
{
var result = _context.Messages.FirstOrDefault(s => s.MessageID == messageID);
if (result != null)
{
result.LastUpdate= DateTime.Now.ToString("HH:mm:ss");
_context.SaveChanges();
}
}
var assert = new Messages();
using (DBContext _context = DbFactory.CreateDbContext())
{
assert = _context.Messages.FirstOrDefault(s => s.MessageID == messageID);
}
return "Last update is: " + assert.LastUpdate;
}
Manage the lifetime of dbContext
You can use IoC(Inversion of Control) containers to manage the lifetime of DBContext such as StructureMap with following steps :
Install nuget package for MVC 4 :
http://nuget.org/packages/StructureMap.MVC4Read quick start :
http://docs.structuremap.net/QuickStart.htmSet scope of your DBContext :
http://docs.structuremap.net/Scoping.htm
Also you can use the combination of Repository and Unit Of Work Patterns for commit or abandon changes on a group of mutations on the dbcontext over multiple requests.
Entity Framework Context lifetime
Create a new class "ClientRepository" in your project. This class will contain all entity framework access logic your client has.
Use this class in all your windows and pages.
If you configure this class to behave as singleton, you will only have one entity framework context.
Ideally this can be done using a DI framework like Microsoft Unit or Ninject, but of course you can also do it manually.
Singltons?
You might ask yourself, whether its a good idea to you singletons here.
I had a similar question once and thus asked Brian Noyes (Microsoft MVP) on a "MVVM" course on the pluralsight website.
In his response he wrote: "...most of my client services are Singletons anyway and live for the life of the app."
So, for a client service, its ok to have a singleton.
How to handle Entity Framework lifetime in a long-running process?
If you're holding on to the injected instance of DbContext and using it in a loop, then clearly the lifetime of DbContext cannot be short.
As you suggest, use a Context Builder in the DI. Here's an example that creates a context of type T using SQLServer:
public class DbContextBuilder<T> where T : DbContext
{
public readonly T Context;
public DbContextBuilder(string connectionStringName)
{
IConfigurationBuilder cfgBuilder = new ConfigurationBuilder();
cfgBuilder.AddJsonFile("appsettings.json");
IConfiguration cfg = cfgBuilder.Build();
DbContextOptionsBuilder<T> optsBuilders = new DbContextOptionsBuilder<T>();
optsBuilders.UseSqlServer(cfg.GetConnectionString(connectionStringName));
Context = (T)Activator.CreateInstance(typeof(T), optsBuilders.Options);
}
}
and in each loop:
foreach (var file in files)
{
using (DbContext ctx = new DbContextBuilder<DbContext>("{name of conn string in appsettings}").Context)
{
// Do you writes for the current file
}
}
How should I manage DbContext Lifetime in MVC Core?
As others already explained, you should use a scoped dependency for database contexts to make sure it will be properly reused. For concurrency, remember that you can query the database asynchronously too, so you might not need actual threads.
If you do need threads, i.e. background workers, then it’s likely that those will have a different lifetime than the request. As such, those threads should not use dependencies retrieved from the request scope. When the request ends and its dependency scope is being closed, disposable dependencies will be properly disposed. For other threads, this would mean that their dependencies might end up getting disposed although they still need them: Bad idea.
Instead, you should explicitly open a new dependency scope for every thread you create. You can do that by injecting the IServiceScopeFactory
and creating a scope using CreateScope
. The resulting object will then contain a service provider which you can retrieve your dependencies from. Since this is a seperate scope, scoped dependencies like database contexts will be recreated for the lifetime of this scope.
In order to avoid getting into the service locator pattern, you should consider having one central service your thread executes that brings together all the necessary dependencies. The thread could then do this:
using (var scope = _scopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetService<BackgroundThreadService>();
service.Run();
}
The BackgroundThreadService
and all its dependency can then follow the common dependency injection way of receiving dependencies.
WPF / EntityFramework Context Lifetime
In your application there is only single unit of work but that is not the purpose of a unit a work. Instead, you need to create a unit of work each time "you work with the database". In your case the UnitOfWork
should not be part of the MEF container but you can create a UnitOfWorkFactory
and inject it from the container. Then the services can create a UnitOfWork
each time "work has to be done" with the database:
using (var unitOfWork = unitOfWorkFactory.Create()) {
// Do work ...
unitOfWork.Save();
}
I have modified UnitOfWork
so it implements IDisposable
. This will allow you to dispose the EF context and also perhaps rollback a transaction if Save
was not called. If you have no need for the extra transaction handling you can even get rid of the UnitOfWork
class because it simply wraps the EF context and instead you can used the EF context as a unit of work directly.
This change will force you to modify how the service and the repositories are structured but you really have to because your issue is that you have a single unit of work that exists for the entire duration of the application.
Related Topics
Server.Mappath - Physical Path Given, Virtual Path Expected
Regex to Match All Us Phone Number Formats
Equivalent of Tuple (.Net 4) for .Net Framework 3.5
Where Are Clr-Defined Methods Like [Delegate].Begininvoke Documented
C# MACro Definitions in Preprocessor
How to Hide Public Methods from Intellisense
Return All Enumerables with Yield Return at Once; Without Looping Through
How to Convert a String Length to a Pixel Unit
How to Add My New User Control to the Toolbox or a New Winform
How to Calculate Divide and Modulo for Integers in C#
Taking Screenshot of a Webpage Programmatically
C# Property and Ref Parameter, Why No Sugar
Global Setting for Asnotracking()
Why Gettype Returns System.Int32 Instead of Nullable<Int32>
No Access to the Session Information Through Signalr Hub. Is My Design Is Wrong
Can You Access UI Elements from Another Thread? (Get Not Set)