NHibernate Утечка соединений / транзакций - PullRequest
0 голосов
/ 11 октября 2011

У нас есть установка NHibernate 3.1 с использованием Fluent NHibernate, а жизнь нашего сеанса управляется с помощью StructureMap 2.6.1. Это в веб-приложении, использующем VB.NET (некоторые проекты на C #).

Мы получаем исключения из производства, из-за которых кажется, что несколько потоков пытаются использовать одно и то же соединение / транзакцию. Это происходит только когда пул подключений включен. Отключение пула соединений очищает эти исключения, но мы наблюдаем значительные проблемы с производительностью, поэтому это временное исправление.

При вызове session.BeginTransaction ()

Серверу не удалось возобновить транзакцию. Описание изделия: 970000004d. Транзакция, активная в этом сеансе, была подтверждена или прервана другим сеансом.

При вызове транзакции.Rollback ()

Транзакция не подключена или была отключена

При попытке внедрить ISession через StructureMap. (Кажется, что это происходит иногда, только когда пул соединений отключен.)

Истекло время ожидания. Период ожидания истек до завершения операции или сервер не отвечает.

Наша NHibernateRegistry for 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 && 
                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;
});

В конце каждого запроса мы очищаем 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

У нас есть метод, которому мы передаем 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();
}

Чтобы предотвратить использование неявных транзакций, мы используем этот метод Transact для операторов SELECT. Вызовы этого метода выглядят так (сеанс вводится через StructureMap с использованием конструктора):

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

Мой вопрос: как мы можем предотвратить совместное использование подключений между несколькими потоками, вызывая исключения выше? Если вам нужна дополнительная информация, пожалуйста, дайте мне знать.

Ответы [ 2 ]

1 голос
/ 12 октября 2011

Ваша проблема в управлении сессиями.Каждый поток должен иметь свой собственный сеанс.Объект сеанса не является потоком сохранения.

0 голосов
/ 12 октября 2011

Я не знаю, в этом ли ваша проблема, но ваш Transact() метод кажется странным. session.Transaction возвращает новую транзакцию, если ее нет в данный момент. Таким образом, ваш session.BeginTransaction() только начинает транзакцию, но не создает ее. Объекты, используемые в using, также должны быть созданы там, а не раньше.

...