Reputation: 5124
I need to update a few tables in my DB in a single transaction and I read that using DbContext.SaveChanges
should be the way to do so.
However I also read that the lifetime of the DbContext
should be as short as possible because it grows over time as it loads more entities.
Also I read that in order to make it thread-safe, each action should have its own DbContext
.
Should I have a DbContext
for each table I want to change and call SaveChanges
on each DbContext
? Wouldn't the last SaveChanges
call override the changes of the previous calls?
What is the best way to do it? (I need this for a website)
Upvotes: 11
Views: 12996
Reputation: 151594
Entity Framework is not thread-safe. An MVC controller is instantiated per request. Thus if you use one DbContext per request, you're safe as long as you don't manually spawn threads in your controller actions (which you shouldn't do anyway).
Now if you have concurrency in your application, like a reservation system where multiple users are out to access the same scarce resources that can run out (like tickets), you'll have to implement logic around that yourself. No thread safety is going to help you there anyway.
That's why you're being asked for code in comments, because explaining thread safety in general is way too broad, and probably not applicable to your situation.
Upvotes: 3
Reputation: 39916
Simple way is, to have one DbContext per request, ASP.NET MVC does all thread safety, each controller instance in ASP.NET MVC is isolated for every request, you don't have to worry about race conditions. As long as you don't create threads and just simply do data transformation in action method using single DbContext, you will not have any problem.
Basically DbContext does nothing, it just queues SQL query to target database, it is the database which handles multi threading, race conditions. To protect your data, you should use transactions and add validations in your database to make sure they are saved correctly
public abstract class DbContextController : Controller{
public AppDbContext DB { get; private set;}
public DbContextController(){
DB = new AppDbContext();
}
protected override void OnDisposing(bool disposing){
DB.Dispose();
}
}
If you inherit any class from DbContextController
and use DB throughout the life of controller, you will not have any problem.
public ActionResult ProcessProducts(){
foreach(var p in DB.Products){
p.Processed = true;
foreach(var order in p.Orders){
order.Processed = true;
}
}
DB.SaveChanges();
}
However, if you use any threads like in following example,
public ActionResult ProcessProducts(){
Parallel.ForEach(DB.Products, p=>{
p.Processed = true;
// this fails, as p.Orders query is fired
// from same DbContext in multiple threads
foreach(var order in p.Orders){
order.Processed = true;
}
});
DB.SaveChanges();
}
Upvotes: 2