.NET Entity Framework and transactions
作为实体框架的新手,我真的很难继续处理这组问题。在我目前正在进行的项目中,整个站点与EF模型紧密集成。首先,使用依赖注入引导程序控制对EF上下文的访问。由于操作原因,我们无法使用DI库。我删除了这个,并在需要时使用了上下文对象的单个实例的模型。我开始出现以下异常:
The type 'XXX' has been mapped more than once.
我们得出的结论是上下文的不同实例导致了这个问题。然后,我将上下文对象抽象为一个单独的静态实例,每个线程/页面都可以访问该实例。我现在得到了几个关于交易的例外情况之一:
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.
最后一个异常发生在加载操作上。我没有试图将上下文状态保存回失败线程上的数据库。然而,有另一个线程执行这样的操作。
这些异常最多是间歇性的,但我已经设法使站点进入一种状态,在这种状态下,由于事务锁,新的连接被拒绝。不幸的是,我找不到异常详细信息。
我想我的第一个问题是,应该从静态的单个实例使用EF模型吗?此外,是否可以消除对EF中事务的需求?我试过使用一个
老实说,我在这里停留了很久,不明白为什么(应该是什么)相当简单的操作会导致这样的问题…
在Web应用程序中创建全局
发生异常是因为您为每个请求创建了一个新事务,但尝试使用相同的
请考虑一下为什么这不起作用。
更新:
另外请注意,每个线程有一个
您可能认为每个线程有一个dbContext实际上是线程安全的,但通常情况并非如此,因为ASP.NET有一个异步模型,允许在不同的线程上完成请求,而不是在其启动的线程上完成请求(最新版本的MVC和Web API甚至允许任意数量的线程处理seq中的单个请求上溯顺序)。这意味着在初始请求完成之前很久,启动请求并创建
当你提到你问题中的"站点"时,我假设这是一个Web应用程序。对于整个应用程序,静态成员只存在一次,如果在整个应用程序中使用具有单个上下文实例的单例类型模式,则所有类型的请求都将在整个应用程序中处于各种状态。
一个静态上下文实例不能工作,但是每个线程有多个上下文实例会很麻烦,而且您不能混合和匹配上下文。您需要的是每个线程一个上下文。我们已经在应用程序中使用依赖注入类型模式完成了这项工作。我们的BLL和DAL类将上下文作为方法中的参数,这样您就可以执行如下操作:
1 2 3 4 5 6 7 | using (TransactionScope ts = new TransactionScope()) { using (ObjectContext oContext = new ObjectContext("MyConnection")) { oBLLClass.Update(oEntity, oContext); } } |
如果需要在更新中调用其他BLL/DAL方法(或您选择的任何方法),只需传递相同的上下文即可。这样,更新/插入/删除是原子的,在一个方法中的所有操作都使用相同的上下文实例,但其他线程没有使用该实例。