2010-07-16 102 views
28

作为新来的实体框架,我真的很困扰如何处理这组问题。在我目前正在进行的项目中,整个网站与EF模型大量集成。首先,使用依赖注入引导程序来控制对EF上下文的访问。出于操作原因,我们无法使用DI库。我删除了它,并根据需要使用了上下文对象的单个实例的模型。我开始得到以下例外:.NET实体框架和事务

类型'XXX'已被映射多次。

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

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

事务操作无法执行,因为有 挂起的请求处理此事务。

当分配给命令的 连接处于未决的本地事务中时,ExecuteReader需要该命令进行事务。 命令的Transaction属性尚未初始化。

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

这些例外是最好的,但我设法让网站进入新的连接被拒绝的状态,由于交易锁定。不幸的是我找不到例外细节。

我想我的第一个问题是,如果从静态单个实例使用EF模型?另外,是否可以删除EF中的事务处理?我一直在使用,但没有成功TransactionScope对象试过......

说实话我很多困在这里,并不能明白为什么(应该是什么)相当简单的操作,造成这样的问题...

+0

相关:http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why – Steven 2012-09-13 09:29:39

+0

这太糟糕了,你不能使用IOC引导程序,因为解决方案与[Ninject](http ://www.ninject.org/)将是一个 “普通的” 实例绑定到_request scope_,如其他人建议:'kernel.Bind >()为了>( ).InRequestScope();' - 是**'InRequestScope' ** – drzaus 2013-07-03 13:56:52

回答

50

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

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

请想想为什么这不起作用。 ObjectContext包含数据库中实体的本地缓存。它允许您进行一系列更改并最终将这些更改提交到数据库。当使用单个静态ObjectContext时,多个用户在该对象上调用SaveChanges,应该如何知道究竟应该提交哪些内容以及哪些不应该提交?因为它不知道,它会保存所有更改,但在那时另一个用户可能仍在进行更改。幸运的是,EF或数据库将失败,因为实体处于无效状态。如果不幸的是,处于无效状态的对象已成功保存到数据库中,并且几星期后可能会发现数据库充满了垃圾。 您的问题的解决方案是create at least one ObjectContext per request。虽然在理论上可以缓存在用户会话对象上下文,这也是一个不错的主意,因为ObjectContext通常会活得太长,并且将包含陈旧的数据(因为其内部缓存不会自动刷新)。

UPDATE

还要注意的是有每个线程一个ObjectContext是为具有完整的Web应用程序的一个单一实例那样糟糕。 ASP.NET使用线程池,这意味着在Web应用程序的生命周期中将创建有限数量的线程。这基本上意味着在那种情况下,那些实例在应用程序的整个生命周期中仍然存在,从而导致数据过时的相同问题。

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

+1

那么重要组成部分,实际上这就是我认为我在做......我绝不会通常使用静态数据的上下文对象,但被越来越绝望。事实证明,我有一个其他的错误,我有单一的上下文实例。然而,它们在一个静态对象内,这似乎是原因。 2个小时我的生活不回来... 谢谢 – sicknote 2010-07-16 15:55:28

+19

只有两个小时?那么你是一个幸运的人:-) – Steven 2010-07-16 21:16:19

+1

什么是面对这种问题的最佳选择? – Romias 2014-03-11 19:12:17

4

至于你提到的“现场”,在你的问题,我认为这是一个Web应用程序。静态成员只存在一次为整个应用程序,如果您使用的在整个应用程序的单一上下文实例单身型图案,请求种种都将是在翻过整个应用程序的各种状态。

单一的静态上下文实例将无法正常工作,但每多线程上下文实例将是麻烦的,以及你不能混搭环境。你需要的是每个线程的单个上下文。我们在应用程序中使用依赖注入类型模式完成了此操作。我们的BLL和DAL类采取了上下文关系中的方法的参数,这样你可以做一些象下面这样:你选择

using (TransactionScope ts = new TransactionScope()) 
{ 
    using (ObjectContext oContext = new ObjectContext("MyConnection")) 
    { 
     oBLLClass.Update(oEntity, oContext); 
    } 
} 

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