NHibernate 泄漏连接/事务
我们使用 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));
}
我的问题是,我们如何阻止连接在多个线程之间共享,从而导致上述异常?如果您需要更多信息,请告诉我。
We have NHibernate 3.1 setup using Fluent NHibernate and our session's life is managed using StructureMap 2.6.1. This is within a Web Application using VB.NET (some projects are in C#).
We're getting exceptions from production that makes it sound like multiple threads are attempting to use the same connection/transaction. These only happen when connection pooling is turned on. Turning connection pooling off clears these exceptions up, but we're seeing significant performance issues, so it's a temporary fix.
When calling session.BeginTransaction()
The server failed to resume the transaction. Desc:970000004d.
The transaction active in this session has been committed or aborted by another session.
When calling transaction.Rollback()
Transaction not connected, or was disconnected
When attempting to inject ISession through StructureMap. (This only seems to happen occasionally when connection pooling is turned off.)
Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.
Our NHibernateRegistry for SturctureMap looks like this:
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;
});
At the end of each request, we clean up StructureMap with the following call in the 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
We have a method that we pass a Func to in order to handle our transactions. This is what that code looks like:
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();
}
In order to prevent using implicit transactions, we use this Transact method for SELECT statements. Calls to this method look like this (the session is injected via StructureMap using constructor injection):
public T Get(TId id)
{
return Transact(() => session.Get<T>(id));
}
My question is, how do we stop connections from being shared between multiple threads, causing the exceptions above? If you need more information, please let me know.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您的问题在于您的会话管理。每个线程都应该有自己的会话。会话对象不是线程保存的。
Your problem is in your session management. Each thread should have its own session. The session object is not thread save.
我不知道这是否是您的问题,但您的
Transact()
方法看起来很奇怪。如果当前没有交易,session.Transaction
将返回一项新交易。因此,您的session.BeginTransaction()
仅启动事务,但不创建它。using
中使用的对象也应该在那里实例化,而不是之前。I don't know if that's your problem, but your
Transact()
method seems weird.session.Transaction
returns a new transaction if there isn't one currently. So yoursession.BeginTransaction()
does only start the transaction, but doesn't create it. Objects used in ausing
should also be instantiated there and not before.