Nhibernate, WinForms, Castle Windsor: Управление сессиями - PullRequest
2 голосов
/ 19 августа 2010

Я знаю, что вопрос управления сессиями поднимался в прошлом, но я не смог найти ничего, что помогло бы мне преодолеть мою проблему ..

У меня есть несколько классов репозитория (например, CustomerRepository, ProductRepository и т. Д.), Которые я решаю с помощью Castle Windsor (Примечание: я пытаюсь применить шаблон трех вызовов, как указано здесь ). Я полагаю, что лучше иметь сеанс для каждого докладчика (в моем случае это эквивалентно одному сеансу для каждой формы), однако классы репозитория должны иметь доступ к сеансу для активной в настоящий момент формы. Я не уверен, как включить это с тем фактом, что эти репозитории разрешаются через виндзор, поскольку докладчики не являются синглетонами ..

Например:

public class SomePresenter
{
  private ISomeView view;
  private ISession session;
  private ICustomerRepository customerRepository;
  private IOrderRepository orderRepository;

  public SomePresenter(ISomeView view, ISessionFactory sessionFactory, ICustomerRepository customerRepository, IOrderRepository orderRepository)
  {
    this.view = view;
    this.session = sessionFactory.OpenSession();
    this.customerRepository = customerRepository;
    this.orderRepository = orderRepository;
  }
}

Хранилищам нужен доступ к сеансу ... Как мне это сделать с помощью Виндзора? Я вынужден вручную устанавливать сеанс в репозиториях через свойство или есть хитрый виндзорский трюк, с которым я незнаком?

Ответы [ 2 ]

4 голосов
/ 21 августа 2010

Почему бы просто не добавить ISession в ваши репозитории вместо ISessionFactory?

Вот аналогичный код, который я использую с Autofac, другим контейнером IoC:

containerBuilder
    .Register(c => NHibernateContext.GetSessionFactory().OpenSession())
    .As<ISession>()
    .InstancePerLifetimeScope();

где NHibernateContext - мой единственный и единственный статический класс, который настраивает NHibernate и поддерживает ISessionFactory синглтон.

Итак, мой объект хранилища / поиска запрашивает сеанс:

public MyRepository(ISession session)
{
    this.session = session;
}

Тогда мой Presenter / View Model / Superivsing Controller / «Какого черта мы это называем в этом месяце» просто получает объект хранилища или поиска:

public MyPresenter(IWhateverRepository repository)
{
     // Look ma, the repository has an ISession and I'm none the wiser!
}

Я думаю, что для Виндзора (я не очень знаком с его API, вам, возможно, придется настроить его, но он должен дать вам представление), это будет что-то вроде

container.Register(
    Component.For<ISession>
    .UsingFactoryMethod(
        x => x.Resolve<ISessionFactory>().OpenSession())
    .LifeStyle.Transient);

То есть вы говорите контейнеру: «Когда кто-то просит ISession, запустите этот маленький делегат, который получает ISessionFactory и открывает сеанс, а затем передайте ему этот ISession экземпляр."

а кто закрывает ISession? Это зависит от вас: вы можете сделать так, чтобы хранилище явно закрывало ISession в своем собственном Dispose() методе. Или вы могли бы положиться на свой контейнер, чтобы сделать закрытие и утилизацию; в Autofac я делаю это с ILifetimeScope и InstancePerLifetimeScope(); в Виндзоре я считаю, что вам нужно искать вложенные контейнеры, чтобы при удалении дочернего контейнера все компоненты, которые он создал, также были удалены.

По моему опыту, это обычно означает, что контейнер просачивается по крайней мере в "основную форму" моего приложения: когда приходит время создавать форму, он создает новую область действия / вложенный контейнер и показывает форму. Но ничего ниже этого уровня не знает о контейнере; это просто бросить лассо вокруг набора компонентов и сказать «избавиться от всего этого, когда форма закрыта».

(Это сделано для того, чтобы не использовать один большой сигнал ISession в большинстве приложений. Это прекрасно работает в ASP.NET, один сеанс на запрос, но, как вы заметили, в Windows Forms это похоже на помечая бомбу замедленного действия для исключений устаревших объектов. Лучше, чтобы каждая «единица работы» (обычно каждая форма или служба) имела свою собственную ISession.)

Вы можете альтернативно спроектировать свои репозитории так, чтобы для каждого метода требовалось передать ISession, но, похоже, это будет утомительно.

Надеюсь, это даст вам некоторые идеи. Удачи!

1 голос
/ 19 августа 2010

Почему бы просто не иметь один SessionProvider с индивидуальным Data Access Objects (DAO) для каждого докладчика / контроллера?Ваша модель доступна через каждый Data Access Object.

public sealed class SessionProvider
{
        static readonly SessionProvider provider = new SessionProvider();
        private static NHibernate.Cfg.Configuration config;
        private static ISessionFactory factory;
        static ISession session = null;

        /// <summary>
        /// Initializes the <see cref="SessionProvider"/> class.
        /// </summary>
        static SessionProvider() { }

        /// <summary>
        /// Gets the session.
        /// </summary>
        /// <value>The session.</value>
        public static ISession Session
        {
            get
            {
                if (factory == null)
                {
                    config = new NHibernate.Cfg.Configuration();
                    config.Configure();

                    factory = config.BuildSessionFactory();
                }

                if (session == null)
                {                   
                    if (config.Interceptor != null)
                        session = factory.OpenSession(config.Interceptor);
                    else
                        session = factory.OpenSession();
                }

                return session;
            }
        }
    }

public sealed class OrderDataControl
{

        private static ILog log = LogManager.GetLogger(typeof(OrderDataControl));

        private static OrderDataControl orderDataControl;
        private static object lockOrderDataControl = new object();
        /// <summary>
        /// Gets the thread-safe instance
        /// </summary>
        /// <value>The instance.</value>
        public static OrderDataControl Instance
        {
            get
            {
                lock (lockOrderDataControl)
                {
                    if (orderDataControl == null)
                        orderDataControl = new OrderDataControl();
                }
                return orderDataControl;
            }           
        }

        /// <summary>
        /// Gets the session.
        /// </summary>
        /// <value>The session.</value>
        private ISession Session
        {
            get
            {
                return SessionProvider.Session;                
            }
        }


        /// <summary>
        /// Saves the specified contact.
        /// </summary>
        /// <param name="contact">The contact.</param>
        /// <returns></returns>
        public int? Save(OrderItems contact)
        {
            int? retVal = null;
            ITransaction transaction = null;

            try
            {
                transaction = Session.BeginTransaction();
                Session.SaveOrUpdate(contact);

                if (transaction != null && transaction.IsActive)
                    transaction.Commit();
                else
                    Session.Flush();

                retVal = contact.Id;
            }
            catch (Exception ex)
            {
                log.Error(ex);
                if (transaction != null && transaction.IsActive)
                    transaction.Rollback();
                throw;
            }

            return retVal;
        }
...