63

I was wondering if the DbContext class is thread safe, I am assuming it's not, as I am currently executing paralell threads that access the DbContext in my application and I am getting a host of locking exceptions and other things that look like they may be thread related.

Until recently I wasn't getting any errors...but until recently I wasn't accessing the DbContext in the threads.

If I am right, what would people suggest as a solution?

jcvandan
  • 13,404
  • 16
  • 60
  • 97

3 Answers3

70

It's not thread safe. Simply create a new instance of DbContext in you thread.

DanielB
  • 18,800
  • 2
  • 41
  • 49
28

No it is not thread safe - whole EF is not thread safe because EF context should never be shared.

Ladislav Mrnka
  • 349,807
  • 56
  • 643
  • 654
  • 2
    So really you should create the dbcontext every time you want to use it? At the moment I just have a dbcontext field as part of my repository class, I suppose creating it when I need it will solve this problem... – jcvandan May 25 '11 at 15:14
  • 2
    You can have one context per repository if a new instance of repository is used by single logical transaction and if your repository is thread safe. – Ladislav Mrnka May 25 '11 at 15:15
  • yeh I guess, to save changes to the dbcontext I am guessing you have to call save changes on the same instance of dbcontext on which you made changes? – jcvandan May 25 '11 at 15:18
  • You don't have to but then you have to deal with detached entities which can be pretty hard. You must detach entities from the first context (it breaks relations) attach them to a new context and say the new context what changes you did. – Ladislav Mrnka May 25 '11 at 15:23
  • 1
    @LadislavMrnka what do u think about locking? I share one instance of DbContext per HTTP request in my MVC application and we have several threads working on one instance. – tugberk Dec 06 '12 at 14:22
  • 1
    As multiple threads accesing one dbcontext is not thread safe you have to lock the multiple read/write access. – HelloWorld May 02 '16 at 14:51
14

Edited - old answer below.

I now always use this pattern with DbContext:

using(var db = new LogDbContext())
{
    // Perform work then get rid of the thing
}

My approach of one per Request Thread meant cached objects in the DbContext would stick around and become stale even while other DbContext instances were writing new values to the actual database behind it. This would create some strange issues of for example one request performing an insert and the next request for the list coming in on a different thread that had a cached, stale list of the data for that query.

There are approaches that make the below work and even improve performance of many-reads/few-writes style apps, but they take more design and strategy than the much simpler pattern above.

Update

I also use a useful helper method for library methods, like logging calls. Here's the helper method:

    public static async Task Using(Db db, Func<Db, Task> action)
    {
        if (db == null)
        {
            using (db = new Db())
            {
                await action(db);
            }
        }
        else
        {
            await action(db);
        }
    }

With this I can easily write code that takes an optional existing DbContext, or instantiates one inside a using context, depending on how it happens to be called.

For example, while working with a DbContext I might load some data, log some info, and then save that data - it's best to do this all with the same DbContext from a performance perspective. On the other hand I might also want to log something in response to a simple action, and neither load nor write any other data. By leveraging the above method I can have just the one logging method that works whether you want to work inside an existing DbContext or not:

public async Task WriteLine(string line, Db _db = null)
{
    await Db.Using(_db, db => {
        db.LogLines.Add(new LogLine(line));
        await db.SaveChangesAsync();
    });
}

Now this method call can be called inside or outside of an existing DbContext and still behave the right way, instead of having to have 2 versions of this and every other convenience logging method or other utility method I have, and instead of having to know and plan for the context of every call that will ever be made to them or their callers. This basically returns to me one of the benefits of the below threadstatic strategy where I didn't have to worry about when exactly the db opened in utility calls that should be worried about it.

Old answer

I usually handle thread-safety with EF DbContext like so:

public class LogDbContext : DbContext
{
    . . .

    [ThreadStatic]
    protected static LogDbContext current;

    public static LogDbContext Current()
    {
        if (current == null)
            current = new LogDbContext();

        return current;
    }

    . . .
}

With this in place I can get a DbContext for this thread like so:

var db = LogDbContext.Current();

It's important to notice that since each DbContext keeps its own local cache, each thread will now have its own separate cache of entity objects, which can introduce some crazy behavior if you're not prepared for it. However, creating new DbContext objects can be expensive, and this approach minimizes that cost.

Chris Moschini
  • 33,398
  • 18
  • 147
  • 176
  • I handle this situation in the same way. But since I am an EF newbie, could you please elaborate on that crazy behaviour you talking about? I'm using this dbcontext in a web forms project, and I dispose each thread's current dbcontext at the Application_EndRequest event. – Thanasis Ioannidis Aug 24 '12 at 16:02
  • 1
    Application_EndRequest isn't the right place to Dispose it because a request won't necessarily be processed on that thread from start to finish; another request might be processed on the same thread before this one completes, dispose the DbContext, and BOOM. The crazy behavior is too long to describe in a Comment, post a separate Question and I'll answer there. – Chris Moschini Aug 24 '12 at 22:18
  • 1
    weird i thought each request gets it's own thread! What is the right place to Dispose a thread static object in a WebForms page lifecylce then? – Thanasis Ioannidis Aug 27 '12 at 08:42
  • 1
    Requests are processed on the ASP.Net Threadpool, which if you google you'll find endless writing on the subject. Basically many requests are processed on any given thread. I don't dispose my DbContext; it manages opening and closing connections under the hood on its own. – Chris Moschini Aug 28 '12 at 21:28
  • So a DbContext only needs to close and dispose its connections? And when exactly this happens? Is it safe to just leave a ThreadStatic DbContext hanging until the GC collects it? I think I read somewhere that the ThreadStatic objects must be manually disposed... is there no problem with that? – Thanasis Ioannidis Aug 30 '12 at 14:09
  • In my usage, either the app is up, or it's dead; I need the DbContext available as long as the app is running. I'm not an expert on this so my usage may be improper: In some places I've read DbContext should live only as long as a Unit of Work, and once that work is done it should be disposed. The cost of instantiating it in EF 4.1 was too high so I moved to ThreadStatic. Instantiation is supposed to be cheaper in EF 4.3 and 5.0. I do know DbContext opens and closes connections for you behind the scenes, so disposing it is about memory caching not connection management. – Chris Moschini Aug 30 '12 at 19:58
  • A discussion of DbContext connection management in EF4.1-5.0: http://blogs.msdn.com/b/diego/archive/2012/01/26/exception-from-dbcontext-api-entityconnection-can-only-be-constructed-with-a-closed-dbconnection.aspx There's a lot of posts even here on StackOverflow that contradict this and suggest there is no behind the scenes connection management and from what I can tell, they are just plain wrong, most likely confusing DbContext's connection management to be identical to ObjectContext's, which it is not. – Chris Moschini Aug 30 '12 at 20:04
  • Thank you, that was helpful actually, and also helped me in some questions I had about Transactions with DbContext! – Thanasis Ioannidis Aug 30 '12 at 23:55
  • After reading this, I've moved to instantiating a new DbContext() each time I need one - the subtle bugs introduced by using one per thread are indeed trouble, and EF5 DbContext instantiation is fast: http://blogs.msdn.com/b/alexj/archive/2009/05/07/tip-18-how-to-decide-on-a-lifetime-for-your-objectcontext.aspx – Chris Moschini Nov 17 '12 at 03:22
  • @ChrisMoschini realize this is an old thread, which is partially what makes it interesting. But the old solution, as you yourself are indicating, seems to be per thread, not per request. Which seems to be what caused your issues. It would be interesting to know how you look at what you wrote in this post today. And if you still use atomic context scopes (something i personally find very cumbersome, especially when going for example multi-tier applications with repositorys or query objects). – Base Nov 25 '17 at 13:13
  • @Baserz I use a lot of library code I've written to make it easier to pass Entities, Db connections, queries, or the like around. I should really write them all up better, but, you can at least find most of them open sourced here under Brass9.Data/Entity - especially EFHelper, DbFactory, B9DbExtender: https://github.com/b9chris/Brass9.Data – Chris Moschini Nov 27 '17 at 00:22