NHibernate泄漏连接/交易

时间:2011-10-11 17:48:39

标签: nhibernate ado.net structuremap

我们使用Fluent NHibernate进行NHibernate 3.1设置,并使用StructureMap 2.6.1管理会话的生命周期。这是在使用VB.NET的Web应用程序中(一些项目在C#中)。

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

调用session.BeginTransaction()

  

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

调用transaction.Rollback()

  

交易未连接或已断开连接

尝试通过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;
});

在每个请求结束时,我们使用Global.asax中的以下调用清理StructureMap:

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();
}

为了防止使用隐式事务,我们对SELECT语句使用此Transact方法。对此方法的调用如下所示(使用构造函数注入通过StructureMap注入会话):

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

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

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

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