Замок Виндзор IoC в приложении MVC - PullRequest
13 голосов
/ 09 декабря 2010

Подготовка к стенке кода ... Это длинное чтение, но оно настолько многословно, насколько я могу.

В ответ на Все еще потеряно в репозиториях и развязке, ASP.NET MVC

Я думаю Я начинаю все ближе понимать все это.Я пытаюсь привыкнуть к этому.Вот что у меня есть.

Project

Project.Web (ASP.NET MVC 3.0 RC)

  • Используется проект.Модели
  • Использует Project.Persistence

Project

Project.Models (доменные объекты)

  • Membership.Member
  • Membership.IMembershipProvider

Project

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

Теперь, в 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();
    }

Сейчасзатем в 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);
    }

Поэтому я спрашиваю прежде всего ..

Я на правильном пути?

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

Так что в основном в моемProject.Persistence layer, я называю SessionFactory следующим образом ..

var session = SessionFactoryContainer.Instance.OpenSession()

Я даже близко к тому, чтобы сделать это правильно?Я все еще в замешательстве - я чувствую, что ISessionFactory должен быть частью Castle.Windsor, но я не могу понять, как это сделать.Меня смущает также то, как я создаю репозиторий в контроллере.Означает ли это, что я должен выполнять все «сопоставления» каждый раз, когда использую репозиторий?Кажется, что это будет очень ресурсоемким.

Ответы [ 2 ]

29 голосов
/ 09 декабря 2010

Сначала некоторые концептуальные детали.В приложении 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);
    }
}

Обратите внимание, что вам не приходилось решать самостоятельно, вы просто указали в контроллере, каковы зависимости.Вы также не указали, как реализованы зависимости - все это разъединено .Это очень просто, здесь нет ничего сложного: -)

Надеемся, что в этот момент вы спрашиваете: "а как конструктор создается?"Именно здесь мы начинаем настраивать ваш контейнер Castle, и мы делаем это полностью в MVC Web проекте (не в постоянстве или домене) .Отредактируйте файл Global.asax, установив Castle Windsor в качестве фабрики контроллеров:

protected void Application_Start()
{
//...   
    ControllerBuilder.Current
        .SetControllerFactory(typeof(WindsorControllerFactory));
}

... и определите 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);
    }
}

Метод 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;
    }
}

... а затем нам нужен каждый из установщиков.ControllerInstaller first:

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

... а вот мой 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;
    }
}

И, наконец, вы захотите определить свой 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
    }
}

Thisдолжно быть достаточно кода, чтобы начать работу! Надеюсь, вы все еще со мной, так как красота контейнера Castle станет очевидна очень скоро.

Когда вы определяете реализацию вашего IMembershipProvider в слое постоянства, помните, что он имеетзависимость от NHibernate ISessionFactory.Все, что вам нужно сделать, это:

public class NHMembershipProvider : IMembershipProvider
{
    private readonly ISessionFactory sessionFactory;

    public NHMembershipProvider(ISessionFactory sessionFactory)
    {
        this.sessionFactory = sessionFactory;
    }
}

Обратите внимание, что, поскольку Castle Windsor создает ваши контроллеры, а провайдеры передаются в ваш конструктор контроллеров, провайдеру автоматически передается реализация ISessionFactory, настроенная в вашем Виндзоре.контейнер!

Вам больше не нужно беспокоиться о создании каких-либо зависимостей снова.Ваш контейнер делает все это автоматически для вас.

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

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

Избегайте использования статического класса IoC, подобного этому.Делая это, вы используете контейнер как сервисный локатор, поэтому вы не достигнете полной развязки инверсии управления.См. эту статью для получения дополнительных пояснений по этому поводу.

Также ознакомьтесь с Sharp Architecture , которая имеет лучшие практики для ASP.NET MVC, NHibernate и Windsor.

Если у вас есть сомнения относительно жизненного цикла самого контейнера, см. Использование контейнеров IoC;в частности Виндзор

...