Castle.Facilities.AutoTx错误的交易问题“非法尝试将集合与两个打开的会话相关联”

时间:2012-09-06 02:52:43

标签: nhibernate transactions castle-windsor

我们在项目中使用了一些Castle设施,具体是Castle Automatic事务和NHibernateIntegration设施。

初始设置是每个方法的事务,但我们遇到了回滚整个工作单元的问题。一切都很好。我们针对每种方法的小事务的Castle配置是:

<component id="SessionModule"
             service="BCS.Modules.ISessionModule, BCS.Modules"
             type="BCS.Modules.SessionModule, BCS.Impl"
             isTransactional="true">     
     <transaction>
        <method name="GetCurrentDate" />
        <method name="GetCurrentDateAndTime" />
     </transaction>
  </component>

然后我们开始在整个请求中遇到回滚操作问题,因此我们决定通过示例将每个业务事务中的所有单一事务分组到父事务中:

namespace BCS.BusinessRules {
   [Transactional]
   public class TransactionModule : ITransactionModule {

      [Transaction( TransactionMode.Requires, IsolationMode.ReadUncommitted, Distributed = false )]
      public Dictionary<String, double> CalculateGoalSetting( Member member, WorkflowContext contextSetting ) { 
       // Calling a lot of child transactional methods for this unit of work
      }

      [Transaction( TransactionMode.Requires, IsolationMode.ReadUncommitted, Distributed = false )]
      public MembershipOperationResult SelfEnrollment( Member member ) {
          // Same here, Another unit of work      
      }

   }

}

在我们的Web应用程序中,每个请求都会调用这些工作单元。但在此之后我们开始出现问题,例如非法尝试将集合与两个开放会话相关联超时已到期。操作完成之前经过的超时时间或服务器没有响应。有时死锁

最常见的错误是:

'/'应用程序中的服务器错误。 非法尝试将集合与两个打开的会话相关联 描述:执行当前Web请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。

异常详细信息:NHibernate.HibernateException:非法尝试将集合与两个打开的会话相关联

来源错误: 在执行当前Web请求期间生成了未处理的异常。可以使用下面的异常堆栈跟踪来识别有关异常的起源和位置的信息。

堆栈追踪:

[HibernateException: Illegal attempt to associate a collection with two open sessions]
   NHibernate.Collection.AbstractPersistentCollection.SetCurrentSession(ISessionImplementor session) +242
   NHibernate.Event.Default.OnUpdateVisitor.ProcessCollection(Object collection, CollectionType type) +177
   NHibernate.Event.Default.AbstractVisitor.ProcessEntityPropertyValues(Object[] values, IType[] types) +83
   NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformUpdate(SaveOrUpdateEvent event, Object entity, IEntityPersister persister) +632
   NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event) +101
   NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event) +365
   NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event) +394
   NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj) +392
   NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything) +578
   NHibernate.Event.Default.AbstractFlushingEventListener.CascadeOnFlush(IEventSource session, IEntityPersister persister, Object key, Object anything) +157
   NHibernate.Event.Default.AbstractFlushingEventListener.PrepareEntityFlushes(IEventSource session) +364
   NHibernate.Event.Default.AbstractFlushingEventListener.FlushEverythingToExecutions(FlushEvent event) +225
   NHibernate.Event.Default.DefaultAutoFlushEventListener.OnAutoFlush(AutoFlushEvent event) +83
   NHibernate.Impl.SessionImpl.AutoFlushIfRequired(ISet`1 querySpaces) +474
   NHibernate.Impl.SessionImpl.List(CriteriaImpl criteria, IList results) +782
   NHibernate.Impl.CriteriaImpl.List(IList results) +63
   NHibernate.Impl.CriteriaImpl.UniqueResult() +69
   BCS.Modules.Behavior.BehaviorElementDataAccess.GetByName(String name) in c:\Projects\dps\bcs\branches\bcs31\BCS.Impl\Modules\Behavior\BehaviorElementDataAccess.cs:41
   BCS.Modules.Behavior.BehaviorManagerModule.FindBehaviorElementByName(String behaviorName) in c:\Projects\dps\bcs\branches\bcs31\BCS.Impl\Modules\Behavior\BehaviorManagerModule.cs:191
   Castle.Proxies.Invocations.IBehaviorManagerModule_FindBehaviorElementByName.InvokeMethodOnTarget() +147
   Castle.DynamicProxy.AbstractInvocation.Proceed() in e:\OSS.Code\Castle.Core\src\Castle.Core\DynamicProxy\AbstractInvocation.cs:144
   Castle.Facilities.AutoTx.TransactionInterceptor.Intercept(IInvocation invocation) in c:\dev\dotnet\castle\main\Castle.Facilities.AutomaticTransactionManagement\src\Castle.Facilities.AutoTx\TransactionInterceptor.cs:92
   Castle.DynamicProxy.AbstractInvocation.Proceed() in e:\OSS.Code\Castle.Core\src\Castle.Core\DynamicProxy\AbstractInvocation.cs:166
   Castle.Proxies.IBehaviorManagerModuleProxy.FindBehaviorElementByName(String behaviorName) +356
   KAO.Behavior.CommonBehaviorCalculations.SaveGoalActivityMinutes(Member member, JulianDate startingDate, JulianDate endingDate, JulianDate currentDate) in c:\svn\qupio21\KAO\Behavior\CommonBehaviorCalculations.cs:148
   KAO.BusinessRules.TransactionModule.CalculateGoalSetting(Member member, WorkflowContext contextSetting) in c:\svn\qupio21\KAO.BusinessRules\TransactionModule.cs:111
   Castle.Proxies.Invocations.ITransactionModule_CalculateGoalSetting.InvokeMethodOnTarget() +155
   Castle.DynamicProxy.AbstractInvocation.Proceed() in e:\OSS.Code\Castle.Core\src\Castle.Core\DynamicProxy\AbstractInvocation.cs:144
   Castle.Facilities.AutoTx.TransactionInterceptor.Intercept(IInvocation invocation) in c:\dev\dotnet\castle\main\Castle.Facilities.AutomaticTransactionManagement\src\Castle.Facilities.AutoTx\TransactionInterceptor.cs:169
   Castle.DynamicProxy.AbstractInvocation.Proceed() in e:\OSS.Code\Castle.Core\src\Castle.Core\DynamicProxy\AbstractInvocation.cs:166
   Castle.Proxies.ITransactionModuleProxy.CalculateGoalSetting(Member member, WorkflowContext context) +183
   BCS.Web.App.Landing.Tunneling.Goals.cmdCalculate_Click(Object sender, EventArgs e) in c:\svn\qupio21\BCS.Web.App\Landing\Tunneling\Goals.aspx.cs:104
   System.Web.UI.WebControls.LinkButton.RaisePostBackEvent(String eventArgument) +155
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3707


Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.272

我们的数据访问层使用NHibernate Integration工具,当前打开的会话:

   public class BehaviorElementDataAccess : DataAccessBase<BehaviorElement, Int64>, IBehaviorElementDataAccess {

      public BehaviorElementDataAccess( ISessionManager sessionManager ) : base( sessionManager ) { }


      public T GetById<T>( long id ) where T : BehaviorElement {
         T ret;
         using ( ISession session = SessionManager.OpenSession( ) ) {
            ret = session.Load<T>( id );
            session.Evict( ret );
         }
         return ret;
      }
}

我们不知道这里如何使用两个会话,在子事务处理完全正常之前,现在我们的并发性显着降低了。 我最初的想法是,使用Castle AutoTX进行事务的默认值是IsolationLevel.ReadCommited,用于那些只在XML配置文件中声明的XML,另一方面是在代码文件中声明的事务,是IsolationLevel.ReadUncommited,所以孩子必须继承这种行为,也为事务共享相同的Hibernate Session。

Nhibernate级别的数据库的隔离级别是否会影响这个级别?我们的设施&gt;中有这个数据库的设置:

 <?xml version="1.0" encoding="utf-8" ?>
<castle>
       <!-- Facilities -->
       <facilities>
          <facility id="atm" type="Castle.Facilities.AutoTx.TransactionFacility, Castle.Facilities.AutoTx">
          </facility>
          <facility id="nhibernate" type="Castle.Facilities.NHibernateIntegration.NHibernateFacility, Castle.Facilities.NHibernateIntegration">
             <factory id="nhibernate.factory" alias="nh.facility.default">
                <settings>
                   <item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
                   <item key="connection.driver_class">NHibernate.Driver.SqlClientDriver</item>
                   <item key="connection.connection_string_name">SQLServerConnection</item>
                   <item key="connection.isolation">ReadCommitted</item>
                   <item key="dialect">NHibernate.Dialect.MsSql2008Dialect</item>
                   <item key="adonet.batch_size">0</item>
                   <item key="use_outer_join">true</item>
                   <item key="query.substitutions">true 1, false 0, yes 'Y', no 'N'</item>
                   <item key="show_sql">true</item>
                   <item key="command_timeout">600</item>
                   <item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
                   <item key="cache.use_minimal_puts">true</item>
                </settings>
                <assemblies>
                   <assembly>BCS.Impl</assembly>
                </assemblies>
                <listeners>
                   <listener type="BCS.NHibernate.Types.ActivitySaveUpdateListener, BCS.Impl" event="Save"/>
                   <listener type="BCS.NHibernate.Types.ActivitySaveUpdateListener, BCS.Impl" event="Update"/>
                   <listener type="BCS.NHibernate.Types.ActivitySaveUpdateListener, BCS.Impl" event="SaveUpdate"/>
                   <listener type="BCS.NHibernate.Types.ActivityDeleteListener, BCS.Impl" event="Delete"/>
                </listeners>

             </factory>
          </facility>
       </facilities>

       <include uri="file://modules.cfg.xml" />

    </castle>

我需要提一下,工作单元回滚工作完美,但有些用户同时得到 NHibernate.HibernateException:非法尝试将一个集合与两个开放会话相关联全部在我们的工作单位,另一方面与父亲的嵌套交易所有这些问题。我们将IIS池更改为ASP.Net 4.0 Classic而没有运气....

我们尝试了不同的东西,但没有任何工作。 我们的Castle二进制版本是:

Castle.Facilities.AutoTx 2.5.1.0
Castle.Facilities.NHibernateIntegration 1.1.0.0
Castle.Services.Transaction 2.5.0.0 
Castle.Windsor 2.5.1.0
NHibernate.ByteCode.Castle 3.1.0.4000 
NHibernate  3.1.0.4000
ASP.Net 4.0

有些人在过去有类似的问题或者有类似的问题吗?

1 个答案:

答案 0 :(得分:0)

在BehaviorElementDataAccess.GetById方法中,您要驱逐加载的实体,这显然会将其从会话中删除。但是,您没有对任何相关实体(特别是此实例中的任何集合)执行任何操作。

我怀疑发生的事情是你正在加载实体,然后NHibernate为它的相关集合创建代理,这些代理保持对会话的引用,然后当你驱逐加载的实体时,代理仍然有这个引用,所以当你试图稍后在同一个会话中再次保存它时,因为实体被驱逐,NHibernate会尝试将实体重新附加到会话中,但是这样做会尝试重新附加已经引用的集合代理。会话,这会导致错误。

我不确定你为什么要驱逐GetById方法中的实体 - 我建议不要这样做,它绕过了NHibernate提供的很多功能。