.NET 实体框架和事务 [英] .NET Entity Framework and transactions

查看:17
本文介绍了.NET 实体框架和事务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

作为实体框架的新手,我真的很纠结于如何处理这组问题.在我目前正在进行的项目中,整个站点都与 EF 模型高度集成.起初,对 EF 上下文的访问是使用依赖注入引导程序控制的.由于操作原因,我们无法使用 DI 库.我删除了它并在需要时使用了上下文对象的单个实例的模型.我开始收到以下异常:

Being new to the Entity Framework, I'm really rather stuck on how to proceed with this set of issues. On the project I am currently working on, the entire site is heavily integrated with the EF model. At first, the access to the EF context was being controlled using an Dependency Injection bootstrapper. For operational reasons we were not able to use an DI library. I removed this and used a model of individual instances of the context object where required. I started getting the following exception:

类型XXX"已被多次映射.

The type 'XXX' has been mapped more than once.

我们得出的结论是,上下文的不同实例导致了这个问题.然后,我将上下文对象抽象为每个线程/页面正在访问的单个静态实例.我现在遇到了有关交易的几个例外之一:

We came to the conclusion that the different instances of the context were causing this issue. I then abstracted the context object into a single static instance which was being accessed by each thread/page. I'm now getting one of several exceptions about transactions:

不允许新事务,因为有其他线程在运行在会话中.

New transaction is not allowed because there are other threads running in the session.

事务操作无法执行,因为有处理此事务的待处理请求.

The transaction operation cannot be performed because there are pending requests working on this transaction.

ExecuteReader 要求命令有一个事务,当分配给命令的连接处于挂起的本地事务中.命令的 Transaction 属性尚未初始化.

ExecuteReader requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.

这些异常中的最后一个发生在加载操作中.我没有尝试将上下文状态保存回失败线程上的 Db.然而,还有另一个线程在执行这样的操作.

The last of these exceptions occurred on a load operation. I wasn't trying to save the context state back to the Db on the thread that failed. There was another thread performing such an operation however.

这些异常充其量只是间歇性的,但我已经设法让站点进入由于事务锁定而拒绝新连接的状态.很遗憾,我找不到异常详细信息.

These exceptions are intermittent at best, but I have managed to get the site to go into a state where new connections were refused due to a transaction lock. Unfortunately I cannot find the exception details.

我想我的第一个问题是,EF 模型应该从静态单个实例中使用吗?另外,是否有可能消除 EF 中的交易需求?我尝试使用 TransactionScope 对象但没有成功...

I guess my first question is, should the EF model be used from a static single instance? Also, is it possible to remove the need for transactions in EF? I've tried using a TransactionScope object without success...

老实说,我一直被困在这里,无法理解为什么(应该是什么)相当简单的操作会导致这样的问题......

To be honest I'm a lot stuck here, and cannot understand why (what should be) fairly simple operations are causing such an issue...

推荐答案

在 Web 应用程序中创建一个全局实体框架 DbContext 非常糟糕.DbContext 类不是线程安全的(对于 Entity Framework v1 的 ObjectContext 类也是如此).它围绕工作单元的概念构建,这意味着您可以使用它来操作单一用例:因此用于业务交易.它旨在处理单个请求.

Creating one global Entity Framework DbContext in a web application is very bad. The DbContext class is not thread-safe (and same holds for Entity Framework v1's ObjectContext class). It is built around the concept of the unit of work and this means you use it to operate a single use case: thus for a business transaction. It is meant to handle one single request.

发生异常是因为您为每个请求创建一个新事务,但尝试使用相同的 DbContext.您很幸运 DbContext 检测到了这一点并抛出了异常,因为现在您发现这行不通.

The exception you get happens because for each request you create a new transaction, but try to use that same DbContext. You are lucky that the DbContext detects this and throws an exception, because now you found out that this won't work.

DbContext 包含数据库中实体的本地缓存.它允许您进行大量更改并最终将这些更改提交到数据库.当使用单个静态 DbContext,多个用户对该对象调用 SaveChanges 时,它如何知道究竟应该提交什么,不应该提交什么?

The DbContext contains a local cache of entities in your database. It allows you to make a bunch of changes and finally submit those changes to the database. When using a single static DbContext, with multiple users calling SaveChanges on that object, how is it supposed to know what exactly should be committed and what shouldn't?

因为它不知道,它会保存所有更改,但此时另一个请求可能仍在进行更改.如果您很幸运,EF 或您的数据库都会失败,因为实体处于无效状态.如果您不走运,处于无效状态的实体会成功保存到数据库中,几周后您可能会发现数据已损坏.

Because it doesn't know, it will save all changes, but at that point another request might still be making changes. When you're lucky, either EF or your database will fail, because the entities are in an invalid state. If you're unlucky, entities that are in an invalid state are successfully saved to the database and you might find out weeks later that your data got corrupted.

您的问题的解决方案是创建至少一个DbContext每个请求.虽然理论上你可以在用户会话中缓存一个对象上下文,但这也是一个坏主意,因为在这种情况下 DbContext 通常会存活太久并且会包含过时的数据(因为它的内部缓存会不会自动刷新).

The solution to your problem is to create at least one DbContext per request. While in theory you could cache an object context in the user session, this also is a bad idea, because in that case the DbContext will typically live too long and will contain stale data (because its internal cache will not automatically be refreshed).

另请注意,每个线程拥有一个 DbContext 与为整个 Web 应用程序拥有一个实例一样糟糕.ASP.NET 使用线程池,这意味着在 Web 应用程序的生命周期内将创建有限数量的线程.这基本上意味着那些 DbContext 实例在这种情况下将在应用程序的整个生命周期中仍然存在,从而导致与数据陈旧相同的问题.

Also note that having one DbContext per thread is about as bad as having one single instance for the complete web application. ASP.NET uses a thread pool which means that a limited amount of threads will be created during the lifetime of a web application. This basically means that those DbContext instances will in that case still live for the lifetime of the application, causing the same problems with staleness of data.

您可能认为每个线程有一个 DbContext 实际上是线程安全的,但通常情况并非如此,因为 ASP.NET 有一个异步模型,允许在不同的线程上完成请求而不是它是从哪里开始的(最新版本的 MVC 和 Web API 甚至允许任意数量的线程按顺序处理单个请求).这意味着启动请求并创建 ObjectContext 的线程可以在初始请求完成之前很久就可以处理另一个请求.但是,该请求中使用的对象(例如网页、控制器或任何业务类)可能仍会引用该 DbContext.由于新的 Web 请求在同一线程中运行,因此它将获得与旧请求使用的相同的 DbContext 实例.这再次导致应用程序中的竞争条件,并导致与全局 DbContext 实例导致的线程安全问题相同的线程安全问题.

You might think that having one DbContext per thread is actually thread-safe, but this is usually not the case, since ASP.NET has an asynchronous model that allows finishing requests on a different thread than where it was started (and the latest versions of MVC and Web API even allow an arbitrary number of threads handle one single request in sequential order). This means that the thread that started a request and created the ObjectContext can become available to process another request long before that initial request finished. The objects used in that request however (such as a web page, controller, or any business class), might still reference that DbContext. Since the new web request runs in that same thread, it will get the same DbContext instance as what the old request is using. This again causes race conditions in your application and cause the same thread-safety issues as what one global DbContext instance causes.

这篇关于.NET 实体框架和事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆