如何使用Entity Framework处理长时间运行的操作?

5
我们的团队正在研发一台机器,用于对装有医学样本小瓶的托盘进行物理处理。该物理过程大约需要1.5小时。托盘和相关小瓶是实体,使用Entity Framework从数据库中加载。在设备运行时,将更新实体上的值。更改可能会在几分钟或几秒钟之间发生。在某些步骤结束时,相隔10至45分钟之间,我们想要将这些实体保存回数据库并继续进行。
在1.5小时内保持Entity Framework上下文打开是否可行?我能否在此期间多次使用该上下文进行更改并保存实体?如果不行,最好的处理方式是什么?
目前有以下一些想法:
- 我们可以使用附加/分离功能,这应该允许我们在上下文之外对实体进行更改,然后创建新的上下文,附加实体以便在想要保存时进行保存,然后分离它以继续工作。 - 每次想要更改实体时,我们都可以创建一个新的上下文。但我认为我们不想每次进行更改时都保存。 - 我们可以将实体复制到业务对象中,并在那里进行更改。然后当我们想要保存时,我们会打开一个上下文,并将更改复制到实体中,然后保存。

个人而言,我会选择选项3,因为它会将数据访问与“业务”问题分离 - 但这是主观的。在数据库角度特别值得问的另一个问题是:可能会更改多少行,以及这些更改的范围是什么(例如,平均更改了多少列)? - Patryk Ćwiek
一个DbContext通常寿命短的原因是它创建起来相对便宜。如果保持长时间存活的上下文,将会填充其ObjectContext与实体,导致逐渐增加的查找时间。但是,如果在您的情况下没有太多的数据库交互,这应该不是问题。我只会在需要时创建上下文并附加现有对象。 - Jeroen Vannevel
@PatrykĆwiek 这个应用程序目前是2层的,所以它直接与实体进行交互。每个“托盘”大约有159行。数据库速度是当前的一个问题 - 即使更新需要几秒钟也没关系。时间主要由物理操作而不是网络或数据库所占据。 - Moby Disk
@JeroenVannevel 幸运的是,该上下文仅用于将相同的对象保存回来。不过,你说得很对 - 如果我们需要提取其他内容,必须确保使用不同的上下文,否则可能会获取陈旧数据或占用内存。 - Moby Disk
@MobyDisk 那么主要问题是您是否想在检查点时保留更改,还是仅在进程完全完成时保留更改。这在一些答案中有所涉及 - 失败恢复也会影响您如何将数据保存到数据库中。 - Patryk Ćwiek
3个回答

3

最好结合2和3使用。

首先,不要将上下文保持打开状态数小时。你可以通过配置实现这一点,但考虑到你只需要运行90分钟的操作,打开连接应该只需要大约3毫秒,这样做只会浪费资源。

因此,在需要时创建一个上下文就可以了。接下来,记住虽然你打开一个上下文来收集数据或维护状态,但如果数据还没有准备好存储,你实际上并不需要保存它。你可以将其仅存储在本地。

这就是第三步,即使用本地内存。基本上,你应该将其保存在本地内存中,并附加事件处理程序。随着本地拷贝的更改,如果更改发生在某个可接受的时间窗口内,则让数据库进行更新。


从一般意义上讲,“这只是浪费资源”。但如果这是一个专门用于特定研究任务的设备,我认为这并不重要。对于高流量网站(或者至少99%的数据库应用程序)来说适用的做法,并不一定适用于这里。 - Eric J.
@EricJ. - 我同意,这通常是浪费资源。显然,根据项目规模绕过最佳实践始终是一个选项。 - Travis J
@EricJ。当你们两个都说“浪费资源”时,似乎是指数据库连接本身。但如果我正确地阅读了https://dev59.com/SXM_5IYBdhLWcg3wq1CF,它并没有一直保持数据库连接开放状态。至于负载,将有不到5台这样的设备同时连接到数据库运行。而且数据库专门为这些设备而设,没有其他用途。 - Moby Disk
每次打开数据库连接时,都会消耗一个连接线程。只有在连接关闭(处理)之前,该线程才会被释放。虽然连接并非始终处于活动状态,但连接线程仍在保持状态。这就是为什么如果您打开了一个连接却没有处理它,就会泄漏连接线程并阻止数据库提供新的连接。 - Travis J
我其实不知道我们最终会做什么,但是因为大家都帮忙了,所以我想接受某人的答案。使用单独的业务对象可能有助于解决多线程问题。在某些情况下,我们将一部分托盘交给不同的“机器人”并行处理。所以我可能还是需要那些对象的。 - Moby Disk
显示剩余2条评论

2
有一个Entity Framework的上下文对象开放1.5小时是否可行?
更新:根据你提供的资源,如果允许EF管理连接的开启和关闭,它会尽可能地延迟开启连接并尽早关闭,以便将相对昂贵的数据库连接返回到连接池。如果您手动管理数据库连接,则只有在上下文对象存在的时候才会保持连接打开状态。
在某些步骤结束时(时间间隔为10到45分钟之间),我们希望将这些实体保存回数据库并继续进行。
请注意,如果客户端因任何原因崩溃,在内存中保存的更改将丢失。在决定是否要在持久化数据之前等待那么长时间时,请考虑其影响。
如果几乎可以确定这是一种架构,其中一个或只有几个客户端写入专用数据库,我会选择尽可能简单的代码......用这种非常特定的情况来换取不重要的资源效率,以减少程序员出错的可能性。

如果我正确理解了https://dev59.com/SXM_5IYBdhLWcg3wq1CF,它不会在整个时间保持数据库连接开放。 - Moby Disk
很有趣...我没有意识到如果连接是自动打开的,它会自动关闭。这更加证明了在编码方面选择最简单的版本的理由。请参见https://dev59.com/YG855IYBdhLWcg3w1oPa以进一步确认您的阅读。 - Eric J.
是的,这使得这成为最简单的方法。 - Moby Disk

1
我知道您希望以批处理的方式保存数据,如果一整个批次没有成功,保存单个值是没有用的。
由于资源不是瓶颈,并且这是一个专用系统,即使上下文存在相对较长时间也无关紧要,因此我会为每个批次使用一个上下文。上下文收集数据并通过一个 SaveChanges 调用结束每个批次,该调用会自动在一个数据库事务中保存一个批次。大致上,代码将如下所示:
do
{
    // Start of a new batch.
    using(var db = new MyContext())
    {
        // Collect data into the context
        ...
        SaveChanges();
    }
} while (....); // While there are new batches

在需要时会打开和关闭数据库连接。 SaveChanges 会执行此操作,但您可能还需要进行其他数据库交互。EF永远不会让连接保持打开状态超过必要的时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接