Castle Windsor IoC在MVC应用程序中

Cie*_*iel 14 asp.net-mvc castle-windsor fluent-nhibernate

准备一堵代码墙...这是一个很长的阅读,但它是我可以得到的冗长.

回应仍然丢失在存储库和解耦,ASP.NET MVC

我开始越来越接近理解这一切了.我正在尝试习惯使用它.这是我到目前为止所拥有的.

项目

Project.Web(ASP.NET MVC 3.0 RC)

  • 使用Project.Models
  • 使用Project.Persistence

项目

Project.Models(域对象)

  • Membership.Member
  • Membership.IMembershipProvider

项目

Project.Persistence(流利的nHibernate)

  • 使用Project.Models
  • 使用Castle.Core
  • 使用Castle.Windsor

  • Membership.MembershipProvider : IMembershipProvider

我有以下课程 Project.Persistence

using Castle.Windsor;

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;

namespace Project.Persistence
{
    public static class IoC
    {
        private static IWindsorContainer _container;

        public static void Initialize()
        {
            _container = new WindsorContainer()
                .Install(
                    new Persistence.Containers.Installers.RepositoryInstaller()
            );
        }

        public static T Resolve<T>()
        {
            return _container.Resolve<T>();
        }
    }
}
namespace Persistence.Containers.Installers
{
    public class RepositoryInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component
                .For<Membership.IMembershipProvider>()
                .ImplementedBy<Membership.MembershipProvider>()
                .LifeStyle.Singleton
            );
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,Project.Web Global.asax Application_Start我有以下代码.

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

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

        // Register the Windsor Container
        Project.Persistence.IoC.Initialize();
    }
Run Code Online (Sandbox Code Playgroud)

现在,Project.Web.Controllers.MembershipController我有以下代码.

    [HttpPost]
    public ActionResult Register( Web.Models.Authentication.Registration model)
    {
        if (ModelState.IsValid)
        {
            var provider = IoC.Resolve<Membership.IMembershipProvider>();
            provider.CreateUser(model.Email, model.Password);
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }
Run Code Online (Sandbox Code Playgroud)

所以我首先要问的是......

我是在正确的轨道上吗?

如何将Castle.Windsor用于我的ISessionFactory

我让SessionFactory像这样工作......

namespace Project.Persistence.Factories
{
    public sealed class SessionFactoryContainer
    {
        private static readonly ISessionFactory _instance = CreateSessionFactory();

        static SessionFactoryContainer()
        { 

        }

        public static ISessionFactory Instance
        {
            get { return _instance; }
        }

        private static ISessionFactory CreateSessionFactory()
        {
            return Persistence.SessionFactory.Map(@"Data Source=.\SQLEXPRESS;Initial Catalog=FluentExample;Integrated Security=true", true);
        }
    }
}
namespace Project.Persistence
{
    public static class SessionFactory
    {
        public static ISessionFactory Map(string connectionString, bool createSchema)
        {
            return FluentNHibernate.Cfg.Fluently.Configure()
                .Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008
                    .ConnectionString(c => c.Is(connectionString)))
                    .ExposeConfiguration(config =>
                    {
                        new NHibernate.Tool.hbm2ddl.SchemaExport(config)
                            .SetOutputFile("Output.sql")
                            .Create(/* Output to console */ false, /* Execute script against database */ createSchema);
                    })
                    .Mappings(m =>
                    {
                        m.FluentMappings.Conventions.Setup(x =>
                        {
                            x.AddFromAssemblyOf<Program>();
                            x.Add(FluentNHibernate.Conventions.Helpers.AutoImport.Never());
                        });

                        m.FluentMappings.AddFromAssemblyOf<Mapping.MembershipMap>();
                    }).BuildSessionFactory();
        }
Run Code Online (Sandbox Code Playgroud)

基本上,在我的Project.Persistence图层中,我像这样调用SessionFactory.

var session = SessionFactoryContainer.Instance.OpenSession()
Run Code Online (Sandbox Code Playgroud)

我甚至接近这样做了吗?我仍然感到困惑 - 我觉得ISessionFactory应该参与其中Castle.Windsor,但我似乎无法弄清楚如何做到这一点.我也对我在Controller中创建存储库的方式感到困惑.这是否意味着我每次使用存储库时都必须执行所有"映射"?这似乎是非常耗费资源的.

csp*_*ton 30

首先是一些概念细节.在ASP.NET MVC应用程序中,页面请求的典型入口点是控制器.我们希望Inversion of Control容器为我们解析控制器,因为控制器所具有的任何依赖关系也可以通过将依赖关系列为控制器构造函数中的参数来自动解析.

困惑了吗?以下是在完成设置后如何使用IoC的示例.我认为这样解释会让事情变得更容易!

public class HomeController : Controller
{
    // lets say your home page controller depends upon two providers
    private readonly IMembershipProvider membershipProvider;
    private readonly IBlogProvider blogProvider;

    // constructor, with the dependencies being passed in as arguments
    public HomeController(
                IMembershipProvider membershipProvider,
                IBlogProvider blogProvider)
    {
        this.membershipProvider = membershipProvider;
        this.blogProvider = blogProvider;
    }

    // so taking your Registration example...
    [HttpPost]
    public ActionResult Register( Web.Models.Authentication.Registration model)
    {
        if (ModelState.IsValid)
        {
            this.membershipProvider.CreateUser(model.Email, model.Password);
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您不必自己解决任何问题,您刚刚在控制器中指定了依赖项.你实际上也没有给出任何关于如何实现依赖关系的指示 - 它们都解耦了.这很简单,这里没什么复杂的:-)

希望在这一点上你要问,"但构造函数如何实例化?" 这是我们开始设置Castle容器的地方,我们完全在MVC Web项目中执行此操作(而不是持久性或域).编辑Global.asax文件,将Castle Windsor设置为控制器工厂:

protected void Application_Start()
{
//...   
    ControllerBuilder.Current
        .SetControllerFactory(typeof(WindsorControllerFactory));
}
Run Code Online (Sandbox Code Playgroud)

...并定义WindsorControllerFactory,以便Windsor实例化您的控制器:

/// Use Castle Windsor to create controllers and provide DI
public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IWindsorContainer container;

    public WindsorControllerFactory()
    {
        container = ContainerFactory.Current();
    }

    protected override IController GetControllerInstance(
        RequestContext requestContext,
        Type controllerType)
    {
        return (IController)container.Resolve(controllerType);
    }
}
Run Code Online (Sandbox Code Playgroud)

ContainerFactory.Current()方法是静态单例,它返回已配置的Castle Windsor容器.容器的配置指示Windsor如何解决应用程序的依赖关系.例如,您可能有一个容器配置为解析NHibernate SessionFactory和您的IMembershipProvider.

我喜欢使用几个"安装程序"配置我的Castle容器.每个安装程序负责不同类型的依赖关系,因此我有一个Controller安装程序,一个NHibernate安装程序,一个Provider安装程序.

首先我们有ContainerFactory:

public class ContainerFactory
{
    private static IWindsorContainer container;
    private static readonly object SyncObject = new object();

    public static IWindsorContainer Current()
    {
        if (container == null)
        {
            lock (SyncObject)
            {
                if (container == null)
                {
                    container = new WindsorContainer();
                    container.Install(new ControllerInstaller());
                    container.Install(new NHibernateInstaller());
                    container.Install(new ProviderInstaller());
                }
            }
        }
        return container;
    }
}
Run Code Online (Sandbox Code Playgroud)

......然后我们需要每个安装程序.第ControllerInstaller一个:

public class ControllerInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(
            AllTypes
                .FromAssembly(Assembly.GetExecutingAssembly())
                .BasedOn<IController>()
                .Configure(c => c.Named(
                    c.Implementation.Name.ToLowerInvariant()).LifeStyle.PerWebRequest));
    }
}
Run Code Online (Sandbox Code Playgroud)

...这是我的,NHibernateInstaller虽然它与你的不同,你可以使用自己的配置.请注意,ISessionFactory每次解析时我都会重复使用相同的实例:

public class NHibernateInstaller : IWindsorInstaller
{
    private static ISessionFactory factory;
    private static readonly object SyncObject = new object();

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        var windsorContainer = container.Register(
            Component.For<ISessionFactory>()
                .UsingFactoryMethod(SessionFactoryFactory));
    }

    private static ISessionFactory SessionFactoryFactory()
    {
        if (factory == null)
        {
            lock (SyncObject)
            {
                if (factory == null)
                {
                    var cfg = new Configuration();
                    factory = cfg.Configure().BuildSessionFactory();
                }
            }
        }

        return factory;
    }
}
Run Code Online (Sandbox Code Playgroud)

最后你要定义你的ProvidersInstaller:

public class ProvidersInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        var windsorContainer = container
            .Register(
                Component
                    .For<IMembershipProvider>()
                    .ImplementedBy<SubjectQueries>())
            .Register(
                Component
                    .For<IBlogProvider>()
                    .ImplementedBy<SubjectQueries>());

            // ... and any more that your need to register
    }
}
Run Code Online (Sandbox Code Playgroud)

这应该是足够的代码来开始!希望你仍然和我在一起,因为Castle容器的美丽很快就会显现出来.

IMembershipProvider在持久层中定义实现时,请记住它依赖于NHibernate ISessionFactory.你需要做的就是:

public class NHMembershipProvider : IMembershipProvider
{
    private readonly ISessionFactory sessionFactory;

    public NHMembershipProvider(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,因为Castle Windsor正在创建您的控制器并且提供程序传递给您的控制器构造函数,所以提供程序会自动传递ISessionFactory您在Windsor容器中配置的实现!

您永远不必担心再次实例化任何依赖项.您的容器会自动为您完成所有操作.

最后,请注意IMembershipProvider应将其定义为域的一部分,因为它定义了域行为的界面.如上所述,处理数据库的域接口的实现被添加到持久层.

  • @Mauricio:所有程序员都受此影响,而不是Castle团队.我没有资格代表整个编程社区发言,但我了解到,大多数程序员忘记了他们不知道如何完成他们必须做的事情的时间,并且在网上搜索中坚持了好几天并拉出头发.我从来没打算让你或者城堡队感到受到攻击,如果我这样做,我会非常抱歉.我是一名新手程序员,在我的工作场所没有同龄人来教育我.Stackoverflow完全是我与其他程序员交谈时最接近的 (2认同)

Mau*_*fer 5

避免使用像这样的静态IoC类.通过这样做,您将容器用作服务定位器,因此您无法实现控制反转的完全解耦.有关此内容的进一步说明,请参阅此文章.

另请参阅Sharp Architecture,它具有ASP.NET MVC,NHibernate和Windsor的最佳实践.

如果您对容器本身的生命周期有疑问,请参阅IoC容器的使用方法; 特别是温莎