C# 在真正复杂的多任务windows窗体程序中锁定并使用实体框架上下文可以吗?

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()) {

我有一个关于EF的具体问题,但这里首先是背景

我继承了一些复杂度已经发展了十多年的代码,我正在慢慢地将其转移到新技术(Linq、任务而不是线程、异步编程……等等),现在轮到删除数据集而支持(异步?)EF了。它是一个Windows窗体,包含数十个任务

具体问题是关于在代码中声明EF上下文的位置,因为关于该主题的文献存在冲突

以下是我能想到的选择:

(1)在任何需要的地方使用/放弃上下文是否更好,如:

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应用程序时,请为每个请求使用上下文实例
  • 当使用Windows演示基础(WPF)或Windows窗体时,使用每个窗体的上下文实例。这允许您使用 更改上下文提供的跟踪功能
  • 如果上下文实例是由依赖项注入容器创建的,则通常由容器负责 处理上下文
  • 如果上下文是在应用程序代码中创建的,请记住在不再需要上下文时处置它
  • 当使用长时间运行的上下文时,考虑以下内容:
    • 当您将更多对象及其引用加载到内存中时,上下文的内存消耗可能会迅速增加。这可能导致 性能问题
    • 该上下文不是线程安全的,因此不应在多个同时处理该上下文的线程之间共享该上下文
    • 如果异常导致上下文处于不可恢复状态,则整个应用程序可能会终止
    • 随着查询和更新数据的时间间隔的增加,遇到并发相关问题的可能性增加 成长
关于上述指南,对于Windows窗体应用程序:

  • 如果需要在表单中进行更改跟踪,则根据从创建上下文实例,并在所有操作中使用相同的上下文

  • 由于使用长时间运行的上下文会产生一些副作用,因此,如果表单中有刷新/重新加载操作,则可以创建一个新的上下文实例并处理以前的实例。如果不需要,请在using块中需要上下文时创建上下文

  • 在处理表单时,不要忘记处理实例

  • 还要记住,上下文不是线程安全的,所以不要跨多个线程共享它,因为多个线程可能同时在上下文上工作


1是的,上下文打开的时间越长,可能发生的问题就越多。2好吧,也许,我的意思是这是一个判断电话,除非你需要,否则不要将上下文保留太久。3不,绝对不是,这是非常糟糕的形式和糟糕的设计。根据经验法则,使用它们可以获得尽可能短的时间和逻辑工作单位。这些都是缓存在下面的,创建新的上下文没有坏处,它们很轻。@TheGeneral你有任何链接来解释为什么(3)不好吗?我有点同意你的看法,但我假设上下文实现了某种内部缓存,因此使用越多(1),我们对该缓存的使用就越少。试图掌握事实:)你保持上下文开放的时间越长,可能发生的问题就越多。而且,创建和跟踪的内容越多,就有很多关于这方面的博客。对不起,我手头没有链接,但这肯定是共识。最后,dbcontext不是线程安全的,这增加了另一层复杂性,可以用1或2轻松解决。标题中提出的问题与您在文章中提出的问题无关。若你们的问题真的是你们在标题中所问的,那个么它就离题了。但是,如果您想了解有关db上下文生存期的一些指导原则(最好有一些官方来源),请阅读我分享的答案。@RezaAghaei,您是对的,我已经更改了标题以准确表达问题的内容。谢谢。这是我之前发现的令人困惑和模糊的文献的一部分:从一个角度来看,似乎暗示我的解决方案编号(3)是可以的,但随后暗示不是。您是否有在多任务环境中使用EF的经验?