NHibernate泄漏连接/事务

NHibernate泄漏连接/事务,nhibernate,ado.net,structuremap,Nhibernate,Ado.net,Structuremap,我们使用Fluent NHibernate设置了NHibernate 3.1,并使用StructureMap 2.6.1管理会话的生命周期。这是一个使用VB.NET的Web应用程序,有些项目是C语言的 我们从生产中得到了异常,这听起来像是多个线程试图使用同一个连接/事务。这些仅在连接池打开时发生。关闭连接池可以清除这些异常,但我们看到了严重的性能问题,所以这是一个临时修复 调用session.BeginTransaction时 服务器无法恢复事务。描述:97000004D。 此会话中活动的事务已

我们使用Fluent NHibernate设置了NHibernate 3.1,并使用StructureMap 2.6.1管理会话的生命周期。这是一个使用VB.NET的Web应用程序,有些项目是C语言的

我们从生产中得到了异常,这听起来像是多个线程试图使用同一个连接/事务。这些仅在连接池打开时发生。关闭连接池可以清除这些异常,但我们看到了严重的性能问题,所以这是一个临时修复

调用session.BeginTransaction时

服务器无法恢复事务。描述:97000004D。 此会话中活动的事务已由另一个会话提交或中止

调用事务时。回滚

事务未连接,或已断开连接

尝试通过StructureMap注入ISession时。这似乎只在连接池关闭时偶尔发生

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

我们针对SturctureMap的NHibernateRegistry如下所示:

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;
});
我们有一个传递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();
}
为了防止使用隐式事务,我们对SELECT语句使用此Transact方法。对此方法的调用如下所示:使用构造函数注入通过StructureMap注入会话:

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

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

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

您的问题在于会话管理。每个线程都应该有自己的会话。会话对象不是线程保存。

不应将StructureMap中ISession变量的作用域设置为HybridHttpOrthReadLocalScope确保每个线程/http请求有一个单独的会话?如果您在WCF环境中通过WCF调用访问,则没有http上下文,线程在多个调用中重复使用。查看NHibernate source,它看起来像session.BeginTransaction调用与session.Transaction相同的属性,因此应该以任意一种方式创建事务。BeginTransaction仅在事务创建后调用开始事务。
public T Get(TId id)
{
    return Transact(() => session.Get<T>(id));
}