Реализация UnitOfWork с помощью Castle.Windsor

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

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

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

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

IUnitOfWork

  • Интерфейс IUnitOfWork используется для объявления шаблона
  • UnitOfWork класс должен Commit и Rollback транзакции, а Expose a 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());
    }

Repository

Теперь я понимаю, что мне нужно передать 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 любого типа (например, когда я пытался, мне сказали, что это тоже плохая практика...).

Confusion - Как создается 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();
    }
}

Ответ 1

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

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

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

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

    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>();

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

Ответ 2

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

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

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

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

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

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