.NET实体框架和交易实体、框架、NET

2023-09-02 10:25:59 作者:澎拜的心

作为新的实体框架,我真的宁愿停留在如何处理这一类问题。在项目我目前工作,整个网站在很大程度上与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.

最后这些异常发生在一个负荷运转。我是不是要救上下文状态恢复到数据库失败的线程上。有然而执行这种操作的另一个线程。

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应用程序中创建一个全球性的的ObjectContext 很糟糕。该的ObjectContext 类不是线程安全的。它是建立在工作的单元的概念,这意味着你用它来操作单个用例:因此,对于单个用户

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

您得到的异常情况,因为每个请求创建一个新的事务,但要尽量使用同一的ObjectContext 。你是幸运的的ObjectContext 检测到这一点,并抛出一个异常,因为现在你发现这是行不通的。

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.

请想一想为什么不能正常工作。该的ObjectContext 包含实体在数据库中的本地缓存。它可以让你做出了一堆修改,最后提交这些更改到数据库。当使用一个单一的静态的ObjectContext ,多个用户调用的SaveChanges 的对象,它是如何应该知道究竟是什么应致力于,什么不应该?因为它不知道,它会保存所有更改,但在这一点上其他用户仍可能更改。如果你运气好或者EF或数据库将失败,因为实体是处于无效状态。如果你是处于无效状态倒霉的对象都成功地保存到数据库中,你可能会在以后发现周你的数据库是充满垃圾。 该问题的解决方案是至少创建一个的ObjectContext 每个请求。虽然理论上可以缓存在用户会话对象上下文,这也是一个不错的主意,因为的ObjectContext 通常会活得太长而将包含陈旧的数据(因为它内部缓存将不会自动刷新)。

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

更新

另外请注意,有一个的ObjectContext 每个线程的那么糟糕,因为有一个单一实例为完整的Web应用程序。 ASP.NET使用线程池,这意味着一个有限的线程量将Web应用程序的生命周期内被创建。这基本上意味着,那些的ObjectContext 实例会在这种情况下仍然生活应用程序的生命周期,从而导致同样的问题与数据过时。

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.

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

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.