Реализация UnitOfWork с помощью Castle.Windsor - PullRequest
11 голосов
/ 17 декабря 2010

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

Как использовать 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();
    }
}

Ответы [ 2 ]

5 голосов
/ 21 декабря 2010

Ваша сессия 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>();

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

0 голосов
/ 24 декабря 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 действительно необходим, только когда транзакция является единственным разумным выбором, но я тоже только начинаю с этого.

...