Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework and Multi threading

We are having some trouble designing our multi-threaded Entity Framework driven application and would like some guidance. We are creating entities on different threads, the entities are added to collections which are then data-bound to various WPF controls. The ObjectContext class is not thread-safe, so managing this we have essentially have 2 solutions:

Solution 1 has a single context and carefully use locking to ensure no 2 threads access it at the same time. This would be relatively simple to implement but would require the context to be alive for the duration of the application. Is it a bad idea to have a single context instance open like this?

Solution 2 is to create context objects on-demand and then detach the objects immediately, then hold them in our own collections, then re-attach them to do any updating. This has some serious problems for use though, as when the objects are detached they lose references to the navigation property objects. Also there is the problem that 2 threads could still try to access a single object, and both try to attach() it to a context. Also, we would need to supply a new context every time we wanted to access entities' navigation properties.

Q: Are either of the two solutions valid, if not how do you recommend we tackle this?

like image 462
Martin Richards Avatar asked Feb 01 '12 16:02

Martin Richards


People also ask

Does Entity Framework support multithreading?

Entity Framework DB contexts are not thread safe. If an ASP.NET Core app wishes to process requests in a multi-threaded way while using Entity Framework Core, it's important to carefully manage DbContexts so that a single context isn't used on multiple threads simultaneously.

Is Entity Framework DbContext thread-safe?

DbContext is not thread-safe. Do not share contexts between threads. Make sure to await all async calls before continuing to use the context instance. An InvalidOperationException thrown by EF Core code can put the context into an unrecoverable state.

Is .NET core multithreaded?

With . NET, you can write applications that perform multiple operations at the same time. Operations with the potential of holding up other operations can execute on separate threads, a process known as multithreading or free threading.

Are entities thread-safe?

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).


2 Answers

First off, I'm assuming you have read the article "Multithreading and the Entity Framework" on MSDN.

Solution #1 is almost certainly the safest from a threading perspective, since you are guaranteeing that only one thread is interacting with the context at any give time. There is nothing inherently wrong with keeping the context around- it isn't keeping database connections open behind the scenes, so it's just memory overhead. This could, of course, present a performance problem if you end up bottlenecked on that thread and the whole application is written with single-db-thread assumptions.

Solution #2 seems unworkable to me- you'd end up with subtle bugs throughout the application where people forget to re-attach (or detach) entities.

One solution is to not use your entity objects in the UI layer of the application. I'd recommend this anyway- chances are, the structure/layout of the entity objects is not optimal for how you want to display things on your user interface (this is the reason for the family of MVC patterns). Your DAL should have methods which are business logic specific (UpdateCustomer for instance), and it should decide internally whether to create a new Context or use a stored one. You can start with the single stored context approach, and then if you run into bottleneck issues you have a limited surface area where you need to make changes.

The downside is that you need to write a lot more code- you'd have your EF entities, but you'd also have business entities that have duplicate properties and potentially differing cardinality of many of the EF entities. To mitigate this, you can use frameworks like AutoMapper to simplify copying properties from the EF entities to the business entities and back again.

like image 126
Chris Shain Avatar answered Oct 02 '22 07:10

Chris Shain


I have seem a dozen of stackoverflow threads concerning EF and multithreading. All of them have answers that explains the problem in depth but not really show you how to fix it.

EF is not thread safe, we all know that by now. But in my experience the only risk is the context creations/manipulations. There is actually a very simple fix for this where you can keep your lazy loading.

Lets say you have a WPF app and a MVC website. where the WPF app uses multihreading. You just dispose the db context in multithreading and keep it when not. Example a MVC website, the context will automaticly disposes after the view has been presented.

In the WPF application layer you use this:

ProductBLL productBLL = new ProductBLL(true); 

In the MVC application layer you use this:

ProductBLL productBLL = new ProductBLL(); 

How your product business logic layer should look like:

public class ProductBLL : IProductBLL {     private ProductDAO productDAO; //Your DB layer      public ProductBLL(): this(false)     {      }     public ProductBLL(bool multiThreaded)     {         productDAO = new ProductDAO(multiThreaded);     }     public IEnumerable<Product> GetAll()     {         return productDAO.GetAll();     }     public Product GetById(int id)     {         return productDAO.GetById(id);     }     public Product Create(Product entity)     {         return productDAO.Create(entity);     }     //etc... } 

How your database logic layer should look like:

public class ProductDAO : IProductDAO {     private YOURDBCONTEXT db = new YOURDBCONTEXT ();     private bool _MultiThreaded = false;      public ProductDAO(bool multiThreaded)     {         _MultiThreaded = multiThreaded;     }     public IEnumerable<Product> GetAll()     {         if (_MultiThreaded)         {             using (YOURDBCONTEXT  db = new YOURDBCONTEXT ())             {                 return db.Product.ToList(); //USE .Include() For extra stuff             }         }         else         {             return db.Product.ToList();         }                       }      public Product GetById(int id)     {         if (_MultiThreaded)         {             using (YOURDBCONTEXT  db = new YOURDBCONTEXT ())             {                 return db.Product.SingleOrDefault(x => x.ID == id); //USE .Include() For extra stuff             }         }         else         {             return db.Product.SingleOrDefault(x => x.ID == id);         }               }      public Product Create(Product entity)     {         if (_MultiThreaded)         {             using (YOURDBCONTEXT  db = new YOURDBCONTEXT ())             {                 db.Product.Add(entity);                 db.SaveChanges();                 return entity;             }         }         else         {             db.Product.Add(entity);             db.SaveChanges();             return entity;         }     }      //etc... } 
like image 29
Eli Avatar answered Oct 02 '22 06:10

Eli