我想知道DbContext
类是否线程安全,我的应用程序正在执行并行线程,并访问DbContext
,导致出现大量锁定异常和其他可能与线程有关的问题。因此,我认为它不是线程安全的。
最近我没有得到任何错误提示...但最近我开始在线程中访问DbContext
。
如果我是正确的,人们会建议什么解决方案?
我想知道DbContext
类是否线程安全,我的应用程序正在执行并行线程,并访问DbContext
,导致出现大量锁定异常和其他可能与线程有关的问题。因此,我认为它不是线程安全的。
最近我没有得到任何错误提示...但最近我开始在线程中访问DbContext
。
如果我是正确的,人们会建议什么解决方案?
它不是线程安全的。只需在您的线程中创建一个新的 DbContext
实例。
不,EF不是线程安全的——整个EF都不是线程安全的,因为EF上下文永远不应该被共享。
编辑 - 以下是旧答案。
现在我总是使用带有DbContext的这种模式:
using(var db = new LogDbContext())
{
// Perform work then get rid of the thing
}
我的“每个请求一个线程”的方法意味着在DbContext中缓存的对象会存在并变得陈旧,即使其他DbContext实例正在向其后面的实际数据库写入新值。这会导致一些奇怪的问题,例如一个请求执行插入,而下一个请求在不同线程上进来时会有一个缓存的、陈旧的数据列表。
有一些方法可以使以下工作正常,并提高多读/少写类型应用程序的性能,但它们需要比以上更简单的模式更多的设计和策略。
更新
我还使用了一个有用的辅助方法来处理库方法,例如日志调用。这是辅助方法:
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);
}
}
使用这个方法,我可以轻松编写代码,根据调用方式的不同,接受一个可选的现有DbContext,或者在using上下文中实例化一个DbContext。
例如,在使用DbContext时,我可能会加载一些数据,记录一些信息,然后保存这些数据——从性能的角度来看,最好使用同一个DbContext完成所有操作。另一方面,我可能还想在响应简单操作时记录某些内容,既不加载也不写入任何其他数据。通过利用上述方法,我可以只有一个日志记录方法,无论您是要在现有的DbContext中工作还是不需要DbContext,都可以使用该方法:
public async Task WriteLine(string line, Db _db = null)
{
await Db.Using(_db, db => {
db.LogLines.Add(new LogLine(line));
await db.SaveChangesAsync();
});
}
现在这个方法可以在现有的DbContext内外被调用,并且仍然能够正确地运行,而不需要拥有两个版本的每个方便日志记录方法或其他实用方法,更不需要知道并计划每个调用它们或其调用者的上下文。这基本上为我带来了以下线程静态策略的一个好处,即我不必担心实用程序调用中数据库何时打开的问题。
我通常像这样处理EF DbContext的线程安全:
public class LogDbContext : DbContext
{
. . .
[ThreadStatic]
protected static LogDbContext current;
public static LogDbContext Current()
{
if (current == null)
current = new LogDbContext();
return current;
}
. . .
}
有了这个东西,我可以像这样为该线程获取一个 DbContext:
var db = LogDbContext.Current();
需要注意的是,由于每个DbContext都有自己的本地缓存,因此每个线程现在将拥有其自己独立的实体对象缓存,如果您没有准备好应对这种情况,则可能会引入一些奇怪的行为。但是创建新的DbContext对象可能是昂贵的,而此方法最小化了这种成本。
dbContext
,然后在父线程中提交更改吗? @DanielB - Peter Isaac