11

简单的问题。

如何将 UnitOfWork 与 Castle.Windsor、nHibernate 和 ASP.NET MVC 一起使用?

现在了解详细信息。在我寻求理解该UnitOfWork模式的过程中,我很难遇到任何使用直接示例与 结合使用的东西Castle.Windsor,特别是关于它需要安装的方式。

到目前为止,这是我的理解。

工作单位

  • IUnitOfWork接口用于声明模式
  • UnitOfWork必须CommitRollback事务,并暴露一个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需要进入 Web 请求级别 - 但我找不到任何解释如何实际执行此操作的内容。我不使用ServiceLocator任何类型的(当我尝试时,有人告诉我这也是不好的做法......)。

困惑——如何UnitOfWork创建一个?

我只是不明白这一点,一般来说。我的想法是我将开始传递UnitOfWorkRepository构造函数 - 但如果它必须进入 Web 请求,我不明白两者之间的关系。

更多代码

这是用于澄清的额外代码,仅仅是因为我似乎有从不为我的问题提供正确信息的习惯。

安装人员

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课堂逐字记录。

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

2 回答 2

5

您的 NH 会话已经是一个工作单元http://nhforge.org/wikis/patternsandpractices/nhibernate-and-the-unit-of-work-pattern.aspx

所以我不确定你为什么需要进一步抽象它。(如果有人读到这个答案知道为什么我会很高兴听到,老实说我从来没有听说过你需要的任何理由......)

我会实现一个简单的每个请求的会话。我不知道你会如何使用 Windsor,因为我从未使用过它,但使用 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();
        }
    }

在 Request_End 的 global.asax 文件中,我调用 ReleaseAndDisposeAllHttpScopedObjects() 方法。

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

因此,当我调用我的第一个存储库时会打开会话,并且当请求结束时它会被处理掉。存储库有一个构造函数,它接受 ISession 并将其分配给一个属性。然后我只是解决回购,如:

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

希望有帮助。还有很多其他方法可以做到这一点,这对我有用。

于 2010-12-21T13:19:15.643 回答
0

图书馆术语与您熟悉的术语不相符,这是语言/阻抗不匹配问题吗?

我对这个 [fluent] nhibernate 也很陌生,所以我仍在尝试弄清楚,但我的看法是:

通常,将 ISession 与应用程序会话相关联(例如,如果它是一个 Web 应用程序,您可能会考虑将会话的创建与 Application_Start 事件相关联,并在应用程序关闭时处理 - 无论是否正常关闭)。当应用程序的范围消失时,存储库也应如此。

UnitOfWork 只是包装/抽象事务的一种方式,在更新期间您有多个操作要执行,并且要保持一致,它们必须按顺序完成,并且每个都成功。例如,在将不只是琐碎的业务规则应用于数据创建、分析或转换时……

这是一篇博客文章的链接,该文章提供了一个以流畅的方式使用 ISession 和 UnitOfWork 的示例。 http://blog.bobcravens.com/2010/06/the-repository-pattern-with-linq-to-fluent-nhibernate-and-mysql/#comments

编辑:只是为了强调,我不认为你 - 必须 - 对存储库的每个操作都使用一个工作单元。只有当事务是唯一合理的选择时才真正需要 UnitOfWork,但我也只是从这个开始。

于 2010-12-24T11:51:48.610 回答