.NET实体框架和事务

sic*_*ote 32 .net entity-framework sql-server-2005

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

"XXX"类型已多次映射.

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

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

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

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

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

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

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

说实话,我在这里很多,并且无法理解为什么(应该是什么)相当简单的操作导致这样的问题......

Ste*_*ven 62

DbContext在Web应用程序中创建全局非常糟糕.该DbContext班是不是线程安全的.它围绕工作单元的概念构建,这意味着您使用它来操作单个用例:因此用于业务事务.它旨在处理一个单一的请求.

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

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

更新:

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

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

  • 只有两个小时?然后你是一个幸运的人:-) (24认同)
  • 面对此类问题的最佳选择是什么? (2认同)

Ben*_*son 6

当您在问题中引用"网站"时,我认为这是一个Web应用程序.静态成员仅对整个应用程序存在一次,如果您在整个应用程序中使用单个类型模式和单个上下文实例,则所有类型的请求将在整个应用程序中处于各种状态.

单个静态上下文实例将不起作用,但每个线程的多个上下文实例将是麻烦的,并且您不能混合和匹配上下文.你需要的是每个线程一个上下文.我们在应用程序中使用依赖注入类型模式完成了此操作.我们的BLL和DAL类将上下文作为方法中的参数,这样您就可以执行以下操作:

using (TransactionScope ts = new TransactionScope())
{
    using (ObjectContext oContext = new ObjectContext("MyConnection"))
    {
        oBLLClass.Update(oEntity, oContext);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您需要在更新中调用其他BLL/DAL方法(或您选择的任何方法),您只需传递相同的上下文.这样,更新/插入/删除是原子的,单个方法中的eveything使用相同的上下文实例,但该实例未被其他线程使用.