9

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)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Idov
  • 5,006
  • 17
  • 69
  • 106
  • If you want to do it all in a single transaction then a single DbContext is fine. No need to worry about thread safety though as you will need to do all your operations sequentially. – DavidG Sep 05 '15 at 08:46
  • 1
    @DavidG : But what if 2 users try to use the same DbContext at the same time? Won't it cause a race condition? – Idov Sep 05 '15 at 08:57
  • You should never allow that to happen. DbContext is not meant to be used that way and will do some very odd things if you try! – DavidG Sep 05 '15 at 08:58
  • @DavidG: This is why i asked if I should use a DbContext for each action. I can use a simple mutex when I use it, but then I might lose some of the optimizations probably built-in... – Idov Sep 05 '15 at 09:01
  • DavidG is correct insert all your records into as many tables as you like. Save then dispose of the db context. It's just not a great idea to keep a db context hanging around and re-using the same one for multiple Web requests. – Steven Yates Sep 05 '15 at 09:06
  • Just create a new db context each time there is a Web request and you'll be fine. You can have multiple instances of a db context running at the same time. Entity-framework can handle that – Steven Yates Sep 05 '15 at 09:09
  • 2
    You really should show some code. I have a feeling you're doing some very bad things as you talk about sharing contexts and needing a mutex, but we can't give any pointed recommendations if we don't see what happens exactly. – Gert Arnold Sep 05 '15 at 09:21
  • @GertArnold: I don't want to use a mutex :) and I still don't have code to show. I just wanted to know which way is the best way to go. – Idov Sep 05 '15 at 09:25

2 Answers2

3

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.

Community
  • 1
  • 1
CodeCaster
  • 147,647
  • 23
  • 218
  • 272
2

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(); 
}
Akash Kava
  • 39,066
  • 20
  • 121
  • 167