Потокобезопасная проблема с Castle.Facilities.NHibernateIntegration ISessionManager в веб-контексте - PullRequest
0 голосов
/ 26 апреля 2011

Поэтому, основываясь на этом вопросе ( здесь ), о котором я спрашивал на прошлой неделе, я решил пойти и взглянуть на проект Castle и использовать средство Castle.Facilities.NHibernateIntegration.

Я провел большую часть двух дней, возясь с этим, и пришел к одной и той же проблеме: сеансы NHibernate-Safe-Safe.Я надеялся, что из коробки встроенный ISessionManager был достаточно умен для обработки потоков, и именно поэтому я решил его реализовать.

В очень редкой документации по этому конкретному проекту упоминается, что вызовISessionManager.OpenSession во многом аналогичен вызову session.GetCurrentSession.Исходя из этого, я понимаю, что у меня нет никакого способа, чтобы открыть новую отдельную сессию.

Так есть ли у меня решение для меня или какие-либо идеи, как я могу работать с этой проблемой?

(Я знаю, что большинство людей скажут, что они работают только с одним потоком, но, честно говоря, нестандартно, некоторые инструменты и процедуры автоматически порождают новый поток. Например, log4net и sessionstatestore. Вы не можете просто предполагать, что будет только одиннить, , связанная , с текущим запросом.)

Примечания:

  • I 'я работаю над веб-моделью с веб-приложением .NET 4.

  • Я вызываю и разрешаю контейнер Windsor обычным, документированным способом и позволяю контейнеру разрешить менеджер сеансов.Я делаю это в обеих темах.

  • Вот моя конфигурация Castle NHibernate:

Код:

<facility id="nhibernate" isWeb="true" type="Castle.Facilities.NHibernateIntegration.NHibernateFacility, Castle.Facilities.NHibernateIntegration">
  <factory id="nhibernate.factory">
    <settings>
      <item key="connection.connection_string">#{NHibernateConnectionString}</item>
      <item key="connection.driver_class">#{NHibernateDriver}</item>
      <item key="connection.provider">NHibernate.Connection.DriverConnectionProvider</item>
      <item key="dialect">#{NHibernateDialect}</item>
      <item key="generate_statistics">true</item>
      <item key="proxyfactory.factory_class">NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle</item>
      <item key="show_sql">true</item>
    </settings>
    <assemblies>
      <assembly>Gigastence.Base.Common</assembly>
    </assemblies>
  </factory>
  • Вот мой пример DAO

Код:

public class NHibernateDao : INHibernateDao
{
    private ISessionManager sessionManager;

    public NHibernateDao(ISessionManager sessionManager)
    {
        this.sessionManager = sessionManager;
    }

    public void Append(LoggingEvent loggingEvent)
    {
        using (IStatelessSession session = sessionManager.OpenStatelessSession())
        {
            using (ITransaction tran = session.BeginTransaction())
            {
                Log data = new Log
                {
                    Id = Guid.NewGuid(),
                    Date = loggingEvent.TimeStamp,
                    Level = loggingEvent.Level.ToString(),
                    Logger = loggingEvent.LoggerName,
                    Thread = loggingEvent.ThreadName,
                    Message = loggingEvent.MessageObject.ToString()
                };

                if (loggingEvent.ExceptionObject != null)
                {
                    data.Exception = loggingEvent.ExceptionObject.ToString();
                }

                session.Insert(data);
                tran.Commit();
            }
        }
    }
}
  • И как я называю DAO. Примечание: Это на недавно порожденной нити, которая не в моих руках.

Код:

public class NHibenateAppender : AppenderSkeleton
{
    protected override void Append(LoggingEvent loggingEvent)
    {
        if(IoC.IsInitialized)
        {
            var NHibernateLogger = IoC.Resolve<INHibernateDao>();
            NHibernateLogger.Append(loggingEvent);
        }
    }
}

Ответы [ 3 ]

0 голосов
/ 04 мая 2011

Посмотрите на эту ссылку!https://github.com/haf/Castle.Facilities.NHibernate/wiki

Это может решить ваши многопоточные проблемы, так как намерение отличается от предыдущего средства;этот позволяет вам хранить сеанс на транзакцию, а не один на запрос.Таким образом, многопоточная проблема исключается, и она будет одинаково хорошо работать с вашим приложением.

В коде это происходит потому, что .Net имеет статический класс CallContext, который знает, в каком потоке вы находитесь(но связывает его с контекстом вашего вызова, а не со статическим потоком).

0 голосов
/ 18 декабря 2013

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

Хитрость, как говорит Джиши; вместо того, чтобы извлекать сессию из Func<ISession> или ISessionManager, вам нужно получить доступ к ISessionFactory.

К сожалению, для меня это было не так просто, как внедрить его через конструктор и заставить Виндзор разрешить его - он не зарегистрирован как часть установщика, как сказал jishi (если я что-то упустил). Однако он доступен через обратный вызов установщика:

public class NHibernateInstaller : INHibernateInstaller, IDatabaseInstaller
{
    ...

    public void Registered(ISessionFactory factory)
    {
        SessionFactoryStore.Set(SessionFactoryKey, factory);
    }
}

Где SessionFactoryStore - это одноэлементное хранилище для хранения ваших фабрик (в случае, если у вас может быть несколько фабрик, распределенных по клиентам, как я).

[Singleton]
public class SessionFactoryStore: ISessionFactoryStore
{
    Dictionary<string, ISessionFactory> SessionFactories { get; set; }

    public SessionFactoryStore()
    {
        SessionFactories = new Dictionary<string, ISessionFactory>();
    }

    public void Set(string key, ISessionFactory factory)
    {
        lock (SessionFactories)
        {
            if (!SessionFactories.ContainsKey(key)) SessionFactories.Add(key, factory);
        }
    }

    public ISessionFactory Get(string key)
    {
        return SessionFactories.ContainsKey(key) ? SessionFactories[key] : null;
    }
}

Затем, где бы вы ни внедрили свой шаблон рабочих единиц или аналогичный, просто выполните тест, чтобы увидеть, работаете ли вы в нормальном или многопоточном состоянии:

[PerWebRequest]
public class UnitOfWork : IUnitOfWork
{
    private IGenericFactory GenericFactory { get; set; }

    private Func<ISession> Session { get; set; }
    private ISessionFactoryStore SessionFactoryStore { get; set; }

    private ISession GetSession(bool isThreaded)
    {
        if (!isThreaded)
            return Session();
        else
            return SessionFactoryStore.Get("YourFactoryKey").OpenSession();
    }

    public UnitOfWork(Func<ISession> session, ISessionFactoryStore sessionFactoryStore) {
        Session = session;
        SessionFactoryStore = sessionFactoryStore;
    }

    ...
}

Привет, потоково-безопасная ISession с использованием NHibernateIntegration.

0 голосов
/ 26 апреля 2011

Если вы хотите получить полный контроль над сеансом, я считаю, что NHibernateFacility фактически регистрирует базовый ISessionFactory в ядре Windsor.

После этого вы можете вызвать sessionFactory.OpenSession(), который, я думаю, должен всегда возвращать новыйсеанс.

Честно говоря, я не очень понимаю, что ISessionManager приносит на вечеринку ...

...