【发布时间】:2019-09-09 17:22:18
【问题描述】:
在我目前正在处理的一个 MVVM WPF 应用程序中,Entity Framework 6 的使用如下:
- 代码优先实体模型
- 在应用程序启动时创建一个 DbContext 并在其间共享(通常在构造函数中传递)
- 某些实体的某些属性直接绑定到 UI - 或者未映射并保存与 UI 相关的内容,例如。在此类实体的构造函数中创建的用户控件
- 有一个单独的线程刷新“作业”供我们的应用程序执行 - 这些“作业”由外部应用程序直接写入数据库
- UI 线程使用相同的 DbContext 来添加、删除或更改那些“作业”和其他实体在 UI 中的点击操作
- 还有另一个单独的线程用于刷新和管理其他实体
- 实体之间利用延迟加载的优势相互链接
首先,我们遇到了 context.SaveChanges() 的问题 - 我们遇到了各种不同的异常,例如:
"New transaction is not allowed because there are other threads running in the session"
"The property "ID" is part of the object's key information and cannot be modified"
"The transaction operation cannot be performed because there are pending requests working on this transaction"
因此,我们在上下文类中为此实现了简单的锁定,希望能解决这个问题:
public override int SaveChanges()
{
lock (this)
{
return base.SaveChanges();
}
}
这只是部分帮助,因为现在我们遇到了以下异常,该异常出现的频率较低:
"An error occurred while saving entities that do not expose foreign key properties for their relationships"
此外,我们有时会遇到链接属性的问题。尽管它们都被定义为虚拟以启用延迟加载,但有时我们确实会收到空引用异常,因为它们不会被链接。
我主要担心的是:
- 经过一些研究,我发现 EF 的这种实现并不像它应该的那样(上下文应该是短暂的)
- 在模型中包含 UI 绑定打破了 SoC 范式
- DbContext 不是线程安全的
我认为理想情况下我们应该重构架构——也许通过开发一些单独的层来处理这些问题,但这在我们的案例中会很耗时,而不是更可取的解决方案。
有没有一种方法可以像在我们的应用程序中设计的那样使用 DbContext 和 EF6,并进行一些更改来解决问题?
【问题讨论】:
-
你在使用 DI 吗?如果是这样,您可以将上下文注册为作用域,以便在请求的生命周期内重复使用。
-
在桌面应用程序中,DbContext 不需要是短暂的,但您需要每个线程单独的 DbContext。但是,您可以拥有一个 DbContext,其生命周期范围为 ViewModel,在桌面应用程序中,它可能长达几分钟。
标签: c# wpf multithreading entity-framework dbcontext