线程安全的实体框架6

时间:2013-11-03 15:07:01

标签: multithreading entity-framework task-parallel-library entity-framework-6

刚开始测试EF6及其异步功能。 当我意识到他们不是线程安全时,男孩感到很惊讶。我有点认为那就是重点。

我已经有了自己的基于Task的扩展方法多年,但我在EF等待的是让它们保持线程安全。

至少我的基于任务的函数lock不会相互干扰。 EF6甚至没有那么远。但主要问题是我的代码与他们的代码共享。即尝试发出异步查询,然后在它完成之前尝试访问导航延迟加载的导航属性(在同一上下文中的预先加载的完全独立的实体上)。这可以由UI,即直接功能之外的其他代码或其他十几种情况触发。

据我所知。 dbContext中仅有的两个共享(实体之间)可变资源是连接和更改跟踪(缓存)。如果我们可以为功能添加锁定,那么我们就会有一个线程安全的上下文。

我们甚至可以分两个阶段完成。如果我们可以实现一个锁定用于查询数据库的集中函数的提供程序。然后,任何非跟踪查询 - 通过返回非实体(匿名)对象或通过调用AsNoTracking() - 将是线程安全的,并且即使另一个线程可能要求延迟加载对象,也可以安全地使用异步函数进行调用。 / p>

我们的可扩展性不会更糟,我们现在必须在每个线程中使用一个上下文,如果你试图跳过一个等待引入一点并行性或正在工作的话,甚至Async函数都不在桌面上在一个事件系统(如wpf)中,一旦等待的函数返回任务,它就可能触发。

所以我的问题是。有没有人实现这样的提供者。或者有人愿意和我一起工作吗?

1 个答案:

答案 0 :(得分:10)

我认为你正面临一个架构问题。您所描述的是UI直接使用EF对象的应用程序,它打破了“关注点分离”范例。

就我而言,我在Model层使用了海关线程安全缓存,让一切都在Model层上发生。我使用众所周知的AsyncLock在我的缓存上实现了线程安全性。

DbContext对象,每个与EF CRUD相关的操作都有非常有限的生命周期。每个CRUD操作都实例化它自己的DbContext,并将模型对象返回到缓存,然后,上下文被垃圾收集。我的应用程序使用缓存作为抽象层,缓存使用EF作为DB抽象层。

例如,在Objects上探索附加属性是通过在Model层上实现自定义方法来完成的,该方法将对象Id作为参数,并将相关对象的列表返回到缓存。 UI询问缓存,然后缓存询问EF,然后一旦可用,对缓存的调用将对象返回到UI。就这么简单。

EntityFramework不是设计为线程安全的,因此无法以多线程方式使用它。 (EF thread safety

您必须构建一个可以多线程方式访问的Model层,而不是并行访问DbContext。并且您的模型可以对您的DB进行多个并行调用,但请记住,每个调用必须实例化并保留它自己的DbContext。在每次调用结束时,必须处理相关的DbContext。

DbContext实际上很快实例化,唯一的缺点是网络延迟。这就是内存缓存是个好主意的原因。