#nhibernate #ado.net #structuremap
#nhibernate #ado.net #Карта структуры
Вопрос:
У нас есть настройка NHibernate 3.1 с использованием Fluent NHibernate, а управление жизненным циклом нашего сеанса осуществляется с помощью StructureMap 2.6.1. Это происходит в веб-приложении с использованием VB.NET (некоторые проекты написаны на C #).
Мы получаем исключения из рабочей среды, из-за которых создается впечатление, что несколько потоков пытаются использовать одно и то же соединение / транзакцию. Это происходит только тогда, когда включен пул соединений. Отключение пула соединений устраняет эти исключения, но мы наблюдаем значительные проблемы с производительностью, поэтому это временное решение.
При вызове сеанса.beginTransaction()
Серверу не удалось возобновить транзакцию. Описание: 970000004d. Транзакция, активная в этом сеансе, была зафиксирована или прервана другим сеансом.
При вызове транзакции.Откат ()
Транзакция не подключена или была отключена
При попытке внедрить ISession через StructureMap. (Кажется, это происходит только изредка, когда объединение пулов соединений отключено.)
Время ожидания истекло. Период ожидания, прошедший до завершения операции, или сервер не отвечает.
Наша 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 amp;amp;
x.GetInterface(typeof(IFakeEntity).Name) == null amp;amp;
typeof(BaseEntity).IsAssignableFrom(x))
.Conventions.AddFromAssemblyOf<ConventionsMarker>()
.UseOverridesFromAssemblyOf<OverridesMarker>()
.OverrideAll(map => map.IgnoreProperties(x => !x.CanWrite amp;amp; !x.Name.EndsWith("Id") amp;amp; !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
У нас есть метод, которому мы передаем функцию для обработки наших транзакций. Вот как выглядит этот код:
protected virtual TResult Transact<TResult>(Func<TResult> func)
{
if (!session.Transaction.IsActive)
{
TResult resu<
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 resu<
}
return func.Invoke();
}
Чтобы предотвратить использование неявных транзакций, мы используем этот метод Transact для операторов SELECT. Вызовы этого метода выглядят следующим образом (сеанс вводится через StructureMap с использованием инъекции конструктора):
public T Get(TId id)
{
return Transact(() => session.Get<T>(id));
}
Мой вопрос в том, как мы можем запретить совместное использование соединений между несколькими потоками, вызывая указанные выше исключения? Если вам нужна дополнительная информация, пожалуйста, дайте мне знать.
Ответ №1:
Ваша проблема заключается в управлении сеансами. Каждый поток должен иметь свой собственный сеанс. Объект сеанса не сохраняется в потоке.
Комментарии:
1. Не следует ли установить для переменной ISession в StructureMap значение HybridHttpOrThreadLocalScoped, чтобы у меня был отдельный сеанс для каждого потока / http-запроса?
2. Если вы находитесь в среде WCF (доступ по вызовам WCF), контекст http отсутствует, и потоки повторно используются для нескольких вызовов.
Ответ №2:
Я не знаю, ваша ли это проблема, но ваш Transact()
метод кажется странным. session.Transaction
возвращает новую транзакцию, если в данный момент ее нет. Таким образом, вы session.BeginTransaction()
только запускаете транзакцию, но не создаете ее. Объекты, используемые в a using
, также должны создаваться там, а не раньше.
Комментарии:
1. Просматривая источник NHibernate, похоже
session.BeginTransaction()
, что он вызывает то же свойство,session.Transaction
что и So, транзакция должна быть создана в любом случае. beginTransaction просто вызываетBegin()
транзакцию после ее создания.