2011-10-11 66 views
0

我们使用Fluent NHibernate进行NHibernate 3.1设置,并使用StructureMap 2.6.1管理会话的生命期。这是使用VB.NET的Web应用程序(有些项目是用C#)。NHibernate泄漏连接/事务

我们从生产中获取异常,听起来像多个线程试图使用相同的连接/事务。这些仅在打开连接池时发生。打开连接池清除这些例外,但我们看到重大的性能问题,所以这是一个暂时的修复。

当调用session.BeginTransaction()

服务器无法恢复交易。说明:970000004d。 此会话中处于活动状态的事务已被另一个会话提交或中止。

当调用transaction.Rollback()

事务未连接,或线路被断开

当试图通过StructureMap注入的Isession。 (这仅在连接池关闭时偶尔发生。)

超时已过期。操作完成之前超时的时间或服务器没有响应。

我们NHibernateRegistry为SturctureMap看起来是这样的:

var dbConfiguration = MsSqlConfiguration.MsSql2008.ConnectionString(ModuleConfig.GetSettings().NHibernateConnectionString) 
    .AdoNetBatchSize(100).IsolationLevel(IsolationLevel.ReadCommitted); 

var cfg = Fluently.Configure() 
    .Database(dbConfiguration) 
    .Mappings(m => 
    { 
     m.FluentMappings.AddFromAssemblyOf<MapMarker>(); 
     m.AutoMappings.Add(AutoMap.AssemblyOf<EntityMarker>() 
      .Where(x => 
       x.GetInterface(typeof(ISubClassEntity).Name) == null && 
       x.GetInterface(typeof(IFakeEntity).Name) == null && 
       typeof(BaseEntity).IsAssignableFrom(x)) 
      .Conventions.AddFromAssemblyOf<ConventionsMarker>() 
      .UseOverridesFromAssemblyOf<OverridesMarker>() 
      .OverrideAll(map => map.IgnoreProperties(x => !x.CanWrite && !x.Name.EndsWith("Id") && !x.PropertyType.IsEnumerable()))); 
    }) 
    .Cache(c => c.UseQueryCache().ProviderClass(typeof(DotNetCacheProvider).AssemblyQualifiedName)); 

cfg.ExposeConfiguration(x => 
{ 
    // custom tuplizers here, removed from snippet. 
    x.SetProperty("adonet.batch_size", "50"); 
}); 

var sessionFactory = cfg.BuildSessionFactory(); 
For<ISessionFactory>().Singleton().Use(sessionFactory); 
For<ISession>().HybridHttpOrThreadLocalScoped().Use(cx => 
{ 
    var session = cx.GetInstance<ISessionFactory>().OpenSession(); 
    session.FlushMode = FlushMode.Commit; 
    session.SetBatchSize(50); 
    return session; 
}); 

在每个请求的结束,我们清理StructureMap与在Global.asax以下电话:

Sub Application_EndRequest(ByVal sender As Object, ByVal e As EventArgs) 
    ' Make sure we dipose of all http scoped objects 
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects() 
End Sub 

我们有一个方法,我们传递一个Func来处理我们的事务。这是代码的样子:

protected virtual TResult Transact<TResult>(Func<TResult> func) 
{ 
    if (!session.Transaction.IsActive) 
    { 
     TResult result; 
     using (var transaction = session.BeginTransaction()) 
     { 
      try 
      { 
       result = func.Invoke(); 
       transaction.Commit(); 
      } 
      catch(Exception ex) 
      { 
       // Make sure the transaction is still active... 
       if(session.Transaction.IsActive) 
       { 
        transaction.Rollback(); 
       } 
       throw new InvalidOperationException("There was an error while executing within an NHibernate Transaction.", ex); 
      } 
     } 
     return result; 
    } 
    return func.Invoke(); 
} 

为了防止使用隐式事务,我们使用这种Transact方法来处理SELECT语句。此方法的调用看起来像这样(会话使用构造器注入通过StructureMap注入):

public T Get(TId id) 
{ 
    return Transact(() => session.Get<T>(id)); 
} 

我的问题是,我们该如何停止从多个线程之间共享的连接,导致上述异常?如果您需要更多信息,请告诉我。

回答

1

你的问题出现在你的会话管理中。每个线程应该有自己的会话。会话对象不是线程保存。

+1

不应该将StructureMap中的ISession变量的范围设置为HybridHttpOrThreadLocalScoped确保每个线程/ http请求都有单独的会话? –

+0

如果您处于WCF环境(通过WCF调用访问),则不存在http上下文,并且线程将在多次调用中重复使用。 – Peter

0

我不知道这是你的问题,但你的Transact()方法似乎很奇怪。 session.Transaction如果当前没有一个返回新的事务。所以你的session.BeginTransaction()只会启动事务,但不会创建它。在using中使用的对象也应该在那里实例化,而不是之前。

+0

翻阅NHibernate源代码,它看起来像'session.BeginTransaction()'调用与'session相同的属性。交易'因此交易应该以任何方式创建。 BeginTransaction只是在事务创建后调用'Begin()'。 –