C# 在真正复杂的多任务windows窗体程序中锁定并使用实体框架上下文可以吗?
我有一个关于EF的具体问题,但这里首先是背景 我继承了一些复杂度已经发展了十多年的代码,我正在慢慢地将其转移到新技术(Linq、任务而不是线程、异步编程……等等),现在轮到删除数据集而支持(异步?)EF了。它是一个Windows窗体,包含数十个任务 具体问题是关于在代码中声明EF上下文的位置,因为关于该主题的文献存在冲突 以下是我能想到的选择: (1)在任何需要的地方使用/放弃上下文是否更好,如:C# 在真正复杂的多任务windows窗体程序中锁定并使用实体框架上下文可以吗?,c#,sql-server,winforms,entity-framework,task,C#,Sql Server,Winforms,Entity Framework,Task,我有一个关于EF的具体问题,但这里首先是背景 我继承了一些复杂度已经发展了十多年的代码,我正在慢慢地将其转移到新技术(Linq、任务而不是线程、异步编程……等等),现在轮到删除数据集而支持(异步?)EF了。它是一个Windows窗体,包含数十个任务 具体问题是关于在代码中声明EF上下文的位置,因为关于该主题的文献存在冲突 以下是我能想到的选择: (1)在任何需要的地方使用/放弃上下文是否更好,如: using (dbEntities context = new dbEntities()) {
using (dbEntities context = new dbEntities()) {
_ratio = (await context.realtime.SingleAsync(x => x.code == _code)).ratio;
}
using (dbEntities context = new dbEntities()) {
... code that does not use the context ...
_ratio = (await context.realtime.SingleAsync(x => x.code == _code)).ratio;
... more code that does not use the context ...
_orders = await context.realtime.Where(x => x.enter).Select(x => x.oderID);
}
(2)每个任务使用上下文是否更好:
using (dbEntities context = new dbEntities()) {
_ratio = (await context.realtime.SingleAsync(x => x.code == _code)).ratio;
}
using (dbEntities context = new dbEntities()) {
... code that does not use the context ...
_ratio = (await context.realtime.SingleAsync(x => x.code == _code)).ratio;
... more code that does not use the context ...
_orders = await context.realtime.Where(x => x.enter).Select(x => x.oderID);
}
(3)将表单中的上下文声明为静态并使用锁访问它是否更好?
... in the global area ...
static public dbEntities context = new dbEntities(); // Declaration and instantiation
static public object dbEntityLock = new object();
... in the code ...
lock (dbEntityLock) {
_ratio = (await context.realtime.SingleAsync(x => x.code == _code)).ratio;
}
(1)和(2)之间的主要区别在于每个任务可能需要几分钟才能完成。即使我将确保在任何地方都需要调用context.SaveChanges(),但将上下文打开这么长时间可以吗
出于安全考虑,不直接使用(1)/(2)的唯一原因是性能:以前的开发人员已经构建了一系列复杂的全局变量(缓存),存储了由其他任务计算的数据,因此逻辑从这些变量中读取数据,而不是从数据库中读取数据,但我相信您可以看到代码变得多么复杂,以确保DB和变量同步。所以,如果可能的话,我也希望删除它,假设EF有一个内部缓存用于它的数据,但只有选项(3)允许我这样做
如果这有助于回答您的问题,那么几乎所有任务都只“负责”数据库中的一个表,它们执行所有计算并将数据存储在数据库中。任务计算的某些值可能需要在其他任务执行的计算中使用,因此使用这些全局变量,而不是在所有任务中不断查询数据库
使用选项(3)安全吗?如果您同意,您会锁定每一次访问(读/写)还是只锁定写访问?有一些关于以下方面的一般准则: 以下是在决定产品寿命时的一些一般准则 背景:
- 使用Web应用程序时,请为每个请求使用上下文实例
- 如果上下文实例是由依赖项注入容器创建的,则通常由容器负责 处理上下文
- 如果上下文是在应用程序代码中创建的,请记住在不再需要上下文时处置它
- 当使用长时间运行的上下文时,考虑以下内容:
- 当您将更多对象及其引用加载到内存中时,上下文的内存消耗可能会迅速增加。这可能导致 性能问题
- 该上下文不是线程安全的,因此不应在多个同时处理该上下文的线程之间共享该上下文
- 如果异常导致上下文处于不可恢复状态,则整个应用程序可能会终止
- 随着查询和更新数据的时间间隔的增加,遇到并发相关问题的可能性增加 成长
- 如果需要在表单中进行更改跟踪,则根据从创建上下文实例,并在所有操作中使用相同的上下文
- 由于使用长时间运行的上下文会产生一些副作用,因此,如果表单中有刷新/重新加载操作,则可以创建一个新的上下文实例并处理以前的实例。如果不需要,请在using块中需要上下文时创建上下文
- 在处理表单时,不要忘记处理实例
- 还要记住,上下文不是线程安全的,所以不要跨多个线程共享它,因为多个线程可能同时在上下文上工作