我们使用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));
}
我的问题是,我们如何阻止连接在多个线程之间共享,导致上述异常?如果您需要更多信息,请告诉我。
答案 0 :(得分:1)
您的问题出在会话管理中。每个线程都应该有自己的会话。会话对象不是线程保存。
答案 1 :(得分:0)
我不知道这是不是你的问题,但你的Transact()
方法似乎很奇怪。如果当前没有新事务,session.Transaction
将返回新事务。所以你的session.BeginTransaction()
只会启动事务,但不会创建它。 using
中使用的对象也应该在那里实例化,而不是之前。