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

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

问题描述

对于实体框架而言,我真的非常坚持如何处理这些问题。在我目前正在开展的项目中,整个网站与EF模型大量整合。首先,使用依赖注入引导程序控制对EF上下文的访问。由于操作原因,我们无法使用DI库。我删除了这个,并在需要时使用上下文对象的单个实例的模型。我开始收到以下异常:


类型XXX已被映射多次。


我们得出结论,上下文的不同实例引起了这个问题。然后,我将上下文对象抽象为一个正在被每个线程/页面访问的静态实例。我现在收到了关于交易的几个例外:


新交易是不允许的,因为有其他线程运行
会话。



无法执行交易操作,因为有
待处理的请求在此事务处理。



当分配给该命令的
连接处于待处理的本地事务中时,ExecuteReader要求该命令具有事务。
该命令的Transaction属性尚未初始化。


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



这些异常最多是间歇性的,但是我设法使网站进入拒绝新连接的状态由于事务锁定。不幸的是我找不到例外细节。



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



说实话我很卡住在这里,不明白为什么(应该是)相当简单的操作导致这样的问题...

解决方案

创建一个全局在web应用程序中的 ObjectContext 是非常糟糕的。 ObjectContext 类不是线程安全的。它围绕工作单位的概念构建,这意味着您可以使用它来运行单个用例:因此用于商业交易。这是为了处理一个单一的请求。



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



请考虑这个为什么这不行。 ObjectContext 包含数据库中实体的本地缓存。它允许您进行一些更改,最后将这些更改提交到数据库。当使用单个静态 ObjectContext ,多个用户在该对象上调用 SaveChanges 时,应该如何知道什么应该承诺什么不应该?因为它不知道,它将保存所有更改,但在这一点上,另一个用户可能仍在进行更改。当您幸运EF或您的数据库将失败,因为实体处于无效状态。如果您处于无效状态的不幸的对象成功保存到数据库中,您可能会在几周后发现您的数据库已经满满的废话了。
您的问题的解决方案是创建至少一个 ObjectContext 每个请求。虽然理论上你可以在用户会话中缓存对象上下文,但这也是一个坏主意,因为 ObjectContext 通常会活得太久,并且会包含过时的数据(因为它的内部缓存不会自动刷新)。



更新



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



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


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:

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 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.

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.

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...

解决方案

Creating a global ObjectContext in a web application is very bad. The ObjectContext class is not thread-safe. 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.

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

Please think about this why this can't work. The ObjectContext 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 ObjectContext, with multiple users calling SaveChanges on that object, how is it supposed to know what exactly should be committed and what shouldn't? Because it doesn't know, it will save all changes, but at that point another user 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 objects that are in an invalid state are successfully saved to the database and you might find out weeks later that your database is full of crap. The solution to your problem is to create at least one ObjectContext per request. While in theory you could cache an object context in the user session, this also is a bad idea, because the ObjectContext will typically live too long and will contain stale data (because its internal cache will not automatically be refreshed).

UPDATE:

Also note that having one ObjectContext per thread is 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 ObjectContext instances will in that case still live for the lifetime of the application, causing the same problems with staleness of data.

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 ObjectContext. Since the new web request runs in that same thread, it will get the same ObjectContext 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 ObjectContext instance causes.

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

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