nhibernate 3.2 thread_static проблема - PullRequest
2 голосов
/ 16 ноября 2011

У меня проблема с NHibernate 3.2.

Я портирую решение, которое мы использовали в приложении Java, на приложение ac # 4.0.Мы хотим создать простой механизм, который обрабатывает сеанс и транзакцию через NHibernate SessionFactory, позволяя инициализировать транзакцию начинающему элементу работы, а затем использоваться всем методом partecipant, даже не зная, что они являются частьюбольшей единицы работы.Но если вы будете вызывать эти вспомогательные методы напрямую, они будут обрабатывать свои собственные транзакции.

В эти вопросы Я лучше объяснил наш подход.Мы впервые сделали это в мире Java, и он работает довольно хорошо.Теперь я переношу тот же подход на c # 4.0, используя NHibernate 3.2.

Класс, который будет обрабатывать все мои сеансы и транзакции, называется OperationManager (вы можете подумать об UnitOfWorkManager):

public class OperationManager : IDisposable
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    ITransaction tx = null;
    ISession session = null;
    bool isInternalTransaction = false;

    public ISession BeginOperation()
    {
        logger.Debug("Thread : " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        session = PersistenceManager.Istance.GetSession();
        if (session.Transaction.IsActive)
        {
            isInternalTransaction = false;
            tx = session.Transaction;                               
            logger.Debug(GetCallerClassDotMethod() + " is binding to transaction " + tx.GetHashCode());
        }
        else
        {
            isInternalTransaction = true;
            tx = session.Transaction;
            tx.Begin(); 
            logger.Debug("Transaction " + tx.GetHashCode() + " created by " + GetCallerClassDotMethod());
        }
        logger.Debug("Session hash " + session.GetHashCode());
        return session;
    }

    private String GetCallerClassDotMethod() {
        // needed to get the Business Logic method calling the public methods
        var st = new StackTrace();
        var sf = st.GetFrame(2);
        var methodReference = sf.GetMethod().Name;
        var classReference = sf.GetMethod().DeclaringType.FullName;
        return string.Concat(classReference, ".", methodReference);
    }

    public void CommitOperation()
    {
        if (isInternalTransaction)
        {                
            tx.Commit();
            logger.Debug(GetCallerClassDotMethod() + " is committing transaction " + tx.GetHashCode());
        }
    }

    public void RollbackOperation()
    {
        if (isInternalTransaction)
        {
            tx.Rollback();                
            logger.Debug(GetCallerClassDotMethod() + " is rolling back transaction " + tx.GetHashCode());                
        }
    }

    public void Dispose()
    {
        tx.Dispose();
        session.Dispose();
    }
}

Вот мой PersistenceManager

internal class PersistenceManager : IDisposable
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
    private static PersistenceManager _istance;
    private ISessionFactory _SessionFactory;
    private static Object _lock = new Object();

    public static PersistenceManager Istance
    {
        get
        {
            lock (_lock)
            {
                if (_istance == null)
                {
                    _istance = new PersistenceManager();
                    logger.Info("New PersistenceManager instance created");
                }
                return _istance;
            }
        }
    }

    private PersistenceManager()
    {
        // Initialize
        Configuration cfg = new Configuration();
        cfg.Configure(ConfigurationManager.ConfigurationManager.Istance.hibernateConfiguration);
        cfg.SetProperty("connection.connection_string", ConfigurationManager.ConfigurationManager.Istance.connectionString);

        /* Note: The AddAssembly() method requires that mappings be 
         * contained in hbm.xml files whose BuildAction properties 
         * are set to ‘Embedded Resource’. */

        // Add class mappings to configuration object
        System.Reflection.Assembly thisAssembly = typeof(PersistenceManager).Assembly;
        cfg.AddAssembly(thisAssembly);            

        // Create session factory from configuration object
        _SessionFactory = cfg.BuildSessionFactory();
    }



    public void Dispose()
    {
        _SessionFactory.Dispose();
    }


    /// <summary>
    /// Close this Persistence Manager and release all resources (connection pools, etc). It is the responsibility of the application to ensure that there are no open Sessions before calling Close().
    /// </summary>
    public void Close()
    {
        _SessionFactory.Close();
    }


    public ISession GetSession()
    {
        return _SessionFactory.OpenSession();
    }

}

Теперь, когда мне нужно получить доступ к БД, я использую экземпляр OperationManager, чтобы получить текущий сеанс (и текущую транзакцию) и использовать его для всех своих нужд.Пример можно найти здесь:

public IList<Agency> getAllAgencies()
    {
        using (var om = new OperationManager())
        {
            try
            {
                om.BeginOperation();
                var result = base.Load<Agency>().ToList();
                om.CommitOperation();
                return result;
            }
            catch (Exception ex)
            {
                om.RollbackOperation();
                throw ex;
            }
        }
    }

и в базовом классе у меня есть

protected IQueryable<T> Load<T>() where T : Model.ModelEntity
    {
        using (var om = new OperationManager())
        {
            try
            {
                var session = om.BeginOperation();
                var entities = session.Query<T>();
                om.CommitOperation();
                return entities;
            }
            catch (Exception ex)
            {
                om.RollbackOperation();
                throw new Exception(msg, ex);
            }
        }
    }

Проблема заключается в том, что даже если я настроил фабрику сеансов NHibernate для работы с каждым потокомВ модели, использующей <property name="current_session_context_class">thread_static</property>, два вызова OperationManager.beginOperation () возвращают сеанс разницы, то есть с другой транзакцией.

Кто-нибудь может сказать мне, почему это происходит?

Отредактировано: Следуя предложению Фреди Требу, я попытался реализовать механизм, который создает новый сеанс или просто получает текущий, используя статический объект CurrentSessionContext из NHibernate.Жаль, это все еще не работает.После очистки кода, избегая всего, что связано с транзакциями, сессиями, единицами работы и т. Д., Я написал очень тривиальный класс и понял, что использование

<property name="current_session_context_class">thread_static</property> 

приносит мне проблемы с sqlсервер 2008 дб.Используя этот класс контекста, используя классический подход SessionFactory.OpenSession () и затем загружая некоторые данные, я получаю следующую ошибку:

System.Data.SqlClient.SqlException: A transport-level error has occurred when receiving results from the server. (provider: Shared Memory Provider, error: 0 - Invalid Handle.)

Есть идеи, почему это произошло?

1 Ответ

2 голосов
/ 16 ноября 2011

Вы каждый раз вызываете SessionFactory.OpenSession ().Это откроет и возвратит новый сеанс независимо от всего остального.

Хотя я не буду стремиться рекомендовать этот подход, чтобы он работал, вам нужно будет сделать какой-то подсчет ссылок на ваш PersistenceManagerчтобы знать, когда открывать новый сеанс и когда его выпускать.

Current_session_context_class не влияет на это, так как он контролирует только то, что возвращает SessionFactory.GetCurrentSession (), а ThreadStaticSessionContext (thread_static) необходимо манипулировать вручнуюв любом случае через Bind / Unbind.

Подход Я бы рекомендовал

Ну, две вещи.

Во-первых, мне нравится определять определенный слой для обработки сессий / транзакций.Я пытаюсь сказать, что если у меня есть методы A и B, и A может использовать B, но B также может использоваться извне, я бы предпочел иметь метод C, который определяет границу сеанса / транзакции для B.

Итак, A и C - это общедоступные методы, и оба используют B (даже когда в C вы почти напрямую вызываете B).

Во-вторых, вам лучше использовать менеджер контекста сеанса, уже доступный, например, тот, который вы можете найти во многих библиотеках на основе NHibernate (например, http://code.google.com/p/unhaddins/) или даже если вы решите продолжитьреализуя свой собственный, попытайтесь сделать так, чтобы он соответствовал механизму SessionContext, доступному в NHibernate, чтобы вы могли вызывать SessionFactory.GetCurrentSession (), чтобы получить сеанс и быть совместимыми с другими контекстами / методами, выполняющими то же самое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...