18

刚刚开始测试 EF6 及其异步功能。当我意识到它们不是线程安全的时,我很惊讶。我有点假设这就是重点。

多年来,我已经有了自己Task的扩展方法,但我一直在等待 EF 使它们成为线程安全的。

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

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

我们甚至可以分两个阶段进行。如果我们可以实现一个提供者来锁定用于查询数据库的一个集中功能。然后,任何未跟踪的查询——无论是通过返回非实体(匿名)对象还是调用 AsNoTracking()——都是线程安全的,并且即使在另一个线程可能要求延迟加载的对象时也可以安全地使用 Async 函数调用。

我们的可扩展性不会比现在更糟,因为我们现在必须为每个线程使用一个上下文,如果您尝试跳过甚至一个 await 以引入一些并行性或在事件中工作,甚至 Async 函数也不在讨论范围内一旦等待的函数与任务一起返回,可能会触发系统(如 wpf)。

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

4

1 回答 1

9

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

在我这边,我在模型层上使用自定义线程安全缓存,让一切都发生在模型层上。我使用众所周知的 AsyncLock 在我的缓存上实现了线程安全。

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

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

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

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

DbContext 实例化非常快,唯一的缺点是网络延迟。这就是为什么内存缓存是一个好主意的原因。

于 2015-01-28T16:04:51.667 回答