Подтвердить что ты не робот

Замок Виндзор IoC в приложении MVC

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

В ответ на Все еще потеряно в репозиториях и развязке, 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
            );
        }
    }
}

Теперь, в 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 я вызываю SessionFactory следующим образом.

var session = SessionFactoryContainer.Instance.OpenSession()

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

4b9b3361

Ответ 1

Во-первых, некоторые концептуальные детали. В приложении 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 в качестве контроллера factory:

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

... и определите WindsorControllerFactory, чтобы ваши контроллеры были созданы Winsor:

/// 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() - это статический singleton, который возвращает сконфигурированный контейнер Castle Windsor. Конфигурация контейнера инструктирует Windsor о том, как разрешить ваши зависимости приложений. Так, например, у вас может быть контейнер, сконфигурированный для разрешения NHibernate SessionFactory, и ваш IMembershipProvider.

Мне нравится настраивать контейнер Castle с помощью нескольких "инсталляторов". Каждый установщик несет ответственность за другой тип зависимости, поэтому у меня был бы установщик Controller, установщик NHibernate, например, установщик поставщика.

Во-первых, у нас есть 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:

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

Этого должно быть достаточно кода для продолжения! Надеюсь, вы все еще со мной, так как красота контейнера замка становится очевидной очень скоро.

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

public class NHMembershipProvider : IMembershipProvider
{
    private readonly ISessionFactory sessionFactory;

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

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

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

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

Ответ 2

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

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

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