Реализация UnitOfWork с Castle.Windsor

Простой вопрос.

Как использовать UnitOfWork с Castle.Windsor, nHibernate и ASP.NET MVC?

Теперь о расширенных деталях. В моем стремлении понять шаблон UnitOfWork мне трудно найти что-либо, в котором используется прямой пример в сочетании с Castle.Windsor, особенно в отношении способа его установки.

Вот мое понимание до сих пор.

IUnitOfWork

  • Интерфейс IUnitOfWork используется для объявления шаблона
  • Класс UnitOfWork должен Commit и Rollback транзакций, а также выставлять Session.

Итак, с учетом сказанного, вот мой IUnitOfWork. (Я использую Fluent nHibernate)

public interface IUnitOfWork : IDisposable
{
    ISession Session { get; private set; }
    void Rollback();
    void Commit();
}

Итак, вот мой Castle.Windsor загрузчик контейнера (ASP.NET MVC)

public class WindsorContainerFactory
{
    private static Castle.Windsor.IWindsorContainer container;
    private static readonly object SyncObject = new object();

    public static Castle.Windsor.IWindsorContainer Current()
    {
        if (container == null)
        {
            lock (SyncObject)
            {
                if (container == null)
                {
                    container = new Castle.Windsor.WindsorContainer();

                    container.Install(new Installers.SessionInstaller());
                    container.Install(new Installers.RepositoryInstaller());
                    container.Install(new Installers.ProviderInstaller());
                    container.Install(new Installers.ControllerInstaller());
                }
            }

        }

        return container;
    }
}

Итак, теперь в моем Global.asax файле у меня есть следующее ...

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);

        // Register the Windsor Container
        ControllerBuilder.Current
            .SetControllerFactory(new Containers.WindsorControllerFactory());
    }

Репозиторий

Теперь я понимаю, что мне нужно передать ISession в мой репозиторий. Итак, позвольте мне предположить IMembershipRepository.

class MembershipRepository : IMembershipRepository
{
   private readonly ISession session;
   public MembershipRepository(ISession session)
   {
      this.session = session;
   }

   public Member RetrieveMember(string email)
   {
      return session.Query<Member>().SingleOrDefault( i => i.Email == email );
   }
}

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

Мне сообщили, что UnitOfWork необходимо перейти на уровень веб-запросов, но я не могу найти ничего, что объясняло бы, как это сделать. Я не использую ServiceLocator какого-либо типа (когда я пытался, мне сказали, что это тоже плохая практика ...).

Путаница - как создается UnitOfWork?

Я вообще этого не понимаю. Я думал, что начну передавать UnitOfWork в Repository конструкторы, но если это должно быть в веб-запросе, я не понимаю, где они связаны.

Дальнейший код

Это дополнительный код для пояснения просто потому, что у меня, кажется, есть привычка никогда не предоставлять правильную информацию по моим вопросам.

Установщики

public class ControllerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            AllTypes.FromThisAssembly()
            .BasedOn<IController>()
            .Configure(c => c.LifeStyle.Transient));
    }
}

public class ProviderInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component
            .For<Membership.IFormsAuthenticationProvider>()
            .ImplementedBy<Membership.FormsAuthenticationProvider>()
            .LifeStyle.Singleton
        );
    }
}

public class RepositoryInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component
            .For<Membership.IMembershipRepository>()
            .ImplementedBy<Membership.MembershipRepository>()
            .LifeStyle.Transient
        );

        container.Register(
            Component
            .For<Characters.ICharacterRepository>()
            .ImplementedBy<Characters.CharacterRepository>()
            .LifeStyle.Transient
        );
    }
}

public class SessionInstaller : Castle.MicroKernel.Registration.IWindsorInstaller
{
    private static ISessionFactory factory;
    private static readonly object SyncObject = new object();

    public void Install(Castle.Windsor.IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            Component.For<ISessionFactory>()
                .UsingFactoryMethod(SessionFactoryFactory)
                .LifeStyle.Singleton
            );

        container.Register(
            Component.For<ISession>()
            .UsingFactoryMethod(c => SessionFactoryFactory().OpenSession())
            .LifeStyle.Transient
        );
    }

    private static ISessionFactory SessionFactoryFactory()
    {
        if (factory == null)
            lock (SyncObject)
                if (factory == null)
                    factory = Persistence.SessionFactory.Map(System.Web.Configuration.WebConfigurationManager.ConnectionStrings["Remote"].ConnectionString);
        return factory;
    }
}

UnitOfWork

Вот мой UnitOfWork класс дословно.

public class UnitOfWork : IUnitOfWork
{
    private readonly ISessionFactory sessionFactory;
    private readonly ITransaction transaction;

    public UnitOfWork(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
        Session = this.sessionFactory.OpenSession();
        transaction = Session.BeginTransaction();
    }

    public ISession Session { get; private set; }

    public void Dispose()
    {
        Session.Close();
        Session = null;
    }

    public void Rollback()
    {
        if (transaction.IsActive)
            transaction.Rollback();
    }

    public void Commit()
    {
        if (transaction.IsActive)
            transaction.Commit();
    }
}

person Ciel    schedule 17.12.2010    source источник


Ответы (2)


Ваша сессия NH уже является единицей работы http://nhforge.org/wikis/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.aspx

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

Я бы реализовал простой сеанс на запрос. Я не знаю, как бы вы сделали это с Виндзором, так как я никогда не использовал его, но с. Это довольно просто с StructureMap.

Я оборачиваю фабрику структурных карт для хранения фабрики сеансов и при необходимости вставляю сеанс в репозитории.

    public static class IoC
    {
        static IoC()
        {
            ObjectFactory.Initialize(x =>
            {
                x.UseDefaultStructureMapConfigFile = false;

                // NHibernate ISessionFactory
                x.ForSingletonOf<ISessionFactory>()
                 .Use(new SessionFactoryManager().CreateSessionFactory());

                // NHibernate ISession
                x.For().HybridHttpOrThreadLocalScoped()
                 .Use(s => s.GetInstance<ISessionFactory>().OpenSession());

                x.Scan(s => s.AssembliesFromApplicationBaseDirectory());
            });

            ObjectFactory.AssertConfigurationIsValid();
        }

        public static T Resolve<T>()
        {
            return ObjectFactory.GetInstance<T>();
        }

        public static void ReleaseAndDisposeAllHttpScopedObjects()
        {
            ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
        }
    }

В файле global.asax на Request_End я вызываю метод ReleaseAndDisposeAllHttpScopedObjects ().

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            IoC.ReleaseAndDisposeAllHttpScopedObjects();
        }

Таким образом, сеанс открывается, когда я вызываю свой первый репозиторий, а когда запрос завершается, он удаляется. В репозиториях есть конструктор, который принимает ISession и назначает его свойству. Затем я просто разрешаю репо, например:

var productRepository = IoC.Resolve<IProductRepository>();

Надеюсь, это поможет. Есть много других способов сделать это, это то, что мне подходит.

person Phill    schedule 21.12.2010
comment
Это действительно помогает - я не собираюсь использовать статический шаблон Singleton, как вы показываете, но я понимаю, что вы говорите. ISession - Единица работы. Мне нужно реализовать логику try и catch, используя ISession в моих репозиториях, вместо того, чтобы пытаться заново изобрести для него колесо. - person Ciel; 29.12.2010
comment
Это просто обертка вокруг моего IoC. Он подключает ISession для каждого запроса. Если вам нужен сеанс для каждого вызова, вы можете в репозитории написать, используя (var session = IoC.Resolve ‹ISessionFactory› () .OpenSession ()) - person Phill; 29.12.2010

Проблема лингвистического несоответствия / несоответствия заключается в том, что термины библиотеки не сочетаются с языком, с которым вы знакомы?

Я тоже новичок в этом [беглом] nhibernate, поэтому я все еще пытаюсь понять это, но мой вывод таков:

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

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

Вот ссылка на сообщение в блоге, в котором приводится пример использования ISession и UnitOfWork в свободном стиле. http://blog.bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/#comments

РЕДАКТИРОВАТЬ: Чтобы подчеркнуть, я не думаю, что вы - должны - использовать единицу работы для каждой операции с репозиторием. UnitOfWork действительно нужен только тогда, когда транзакция - единственный разумный выбор, но я тоже только начинаю с этого.

person JustinC    schedule 24.12.2010