Is DbContext thread safe?

C#Thread SafetyEntity Framework-4.1

C# Problem Overview


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?

C# Solutions


Solution 1 - C#

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

Solution 2 - C#

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

Solution 3 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjcvandanView Question on Stackoverflow
Solution 1 - C#DanielBView Answer on Stackoverflow
Solution 2 - C#Ladislav MrnkaView Answer on Stackoverflow
Solution 3 - C#Chris MoschiniView Answer on Stackoverflow