Castle Windsor IoC w aplikacji MVC

Przygotuj się na ścianę kodu... To długa lektura, ale tak gadatliwa, jak tylko mogę.

W odpowiedzi na nadal utracone na repozytoriach i oddzielenie, ASP.NET MVC

Myślę zaczynam zbliżać się do zrozumienia tego wszystkiego. Staram się przyzwyczaić do tego. Oto, co mam do tej pory.

Projekt

Projekt.Www (ASP.NET MVC 3.0 RC)

  • Używa Projektu.Modele
  • zastosowania Projekt.Persistence

Projekt

Projekt.Models (Domain Objects)

  • Membership.Member
  • Membership.IMembershipProvider

Projekt

Projekt.Persistence (Fluent nHibernate)

  • Używa Projektu.Modele
  • Używa Castle ' A.Rdzeń
  • Używa Castle ' A.Windsor

  • Membership.MembershipProvider : IMembershipProvider

Mam następującą klasę w 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
            );
        }
    }
}

Teraz, w Project.Web Global.asax Application_Start, I mieć następujący kod.

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

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

        // Register the Windsor Container
        Project.Persistence.IoC.Initialize();
    }

Teraz, w Project.Web.Controllers.MembershipController Mam następujący kod.

    [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);
    }
Więc pytam przede wszystkim..

Czy jestem na dobrej drodze?

Jak mogę używać Castle ' a.Windsor for my ISessionFactory

Moja SessionFactory działa w ten sposób ...

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

Więc zasadniczo, w mojej warstwie Project.Persistence nazywam SessionFactory tak..

var session = SessionFactoryContainer.Instance.OpenSession()
Czy w ogóle zbliżam się do zrobienia tego dobrze? Wciąż jestem zdezorientowany - czuję się jak ISessionFactory powinny być częścią Castle.Windsor, ale nie wiem, jak to zrobić. Jestem również zdezorientowany sposobem tworzenia repozytorium w kontrolerze. Czy to oznacza, że muszę wykonywać wszystkie "mapowania" za każdym razem, gdy korzystam z repozytorium? Wydaje się, że byłoby to bardzo zasobochłonne.
Author: Community, 2010-12-09

2 answers

Po pierwsze pewne konceptualne szczegóły. W ASP.NET aplikacja MVC typowym punktem wejścia dla żądania strony jest kontroler. Chcemy, aby Inwersja kontenera kontrolnego rozwiązywała nasze kontrolery za nas, ponieważ wtedy wszelkie zależności, które mają kontrolery, mogą być również automatycznie rozwiązywane po prostu wymieniając zależności jako argumenty w konstruktorach kontrolerów.

Zdezorientowany jeszcze? Oto przykład użycia IoC po jego skonfigurowaniu. Myślę, że wyjaśniając to w ten sposób wszystko jest łatwiejsze!

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

Zauważ, że nie musisz samodzielnie rozwiązywać problemów, po prostu określiłeś w kontrolerze, jakie są zależności. Nie podałeś też żadnych wskazówek, jak zaimplementowane są Zależności - to wszystko jest odsprzęgnięte. To bardzo proste nie ma tu nic skomplikowanego: -)

Mam nadzieję, że w tym momencie pytasz: "ale w jaki sposób konstruktor zostaje utworzony?"Tu zaczynamy urządzać Twój zamek kontener, a robimy to w całości w MVC Web project (nie Persistence czy Domain) . Edycja globalna.plik asax, ustawiający Castle Windsor jako fabrykę kontrolerów:

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

...i zdefiniuj WindsorControllerFactory tak, aby Twoje Kontrolery były utworzone przez 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);
    }
}

Metoda ContainerFactory.Current() jest statycznym singletonem, który zwraca skonfigurowany kontener Castle Windsor. Konfiguracja kontenera instruuje jak rozwiązać swój zależności aplikacji. Na przykład możesz mieć kontener skonfigurowany do rozwiązywania NHibernate SessionFactory i imembershipprovider.

Lubię konfigurować mój kontener Castle używając kilku "instalatorów". Każdy instalator odpowiada za inny typ zależności, więc miałbym instalatora kontrolera , instalatora NHibernate , Instalatora dostawcy .

Po pierwsze mamy 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;
    }
}

...a potem potrzebujemy każdego z instalatorów. ControllerInstaller pierwszy:

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

... a oto mój NHibernateInstaller choć różni się od Twojego, możesz użyć własnej konfiguracji. Zauważ, że używam ponownie tej samej instancji ISessionFactory za każdym razem, gdy zostanie rozwiązana:

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

I na koniec będziesz chciał zdefiniować swoje 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
    }
}

To powinno wystarczyć, aby zacząć! mam nadzieję, że nadal jesteś ze mną jako piękno pojemnika Zamku staje się oczywiste bardzo szybko.

Kiedy definiujesz swoją implementację IMembershipProvider w warstwie persistence, pamiętaj, że ma ona zależność od NHibernate ISessionFactory. Wszystko co musisz zrobić to:

public class NHMembershipProvider : IMembershipProvider
{
    private readonly ISessionFactory sessionFactory;

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

Zauważ, że ponieważ Castle Windsor tworzy twoje Kontrolery, a dostawcy są przekazywani do twojego konstruktora kontrolerów, dostawca jest automatycznie przekazywany ISessionFactory implementacji skonfigurowanej w Twoim kontenerze Windsor!

Nigdy nie musisz martw się o ponowne utworzenie jakichkolwiek zależności. Twój kontener zrobi to automatycznie za Ciebie.

Na koniec zauważ, że {[13] } powinien być zdefiniowany jako część twojej domeny, ponieważ definiuje interfejs dla zachowania Twojej domeny. Jak wspomniano powyżej, implementacja interfejsów domenowych, które zajmują się bazami danych, jest dodawana do warstwy trwałości.

 26
Author: cspolton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-16 18:13:42

Unikaj używania statycznej klasy IoC takiej jak ta. W ten sposób używasz kontenera jako lokalizatora usług, więc nie osiągniesz pełnego odsprzęgnięcia inwersji sterowania. Zobacz Ten artykuł aby uzyskać dalsze wyjaśnienia na ten temat.

Sprawdź również Sharp Architecture , która ma najlepsze praktyki dla ASP.NET MVC, NHibernate i Windsor.

Jeśli masz wątpliwości co do cyklu życia samego kontenera, zobacz wykorzystanie kontenerów IoC; w szczególności Windsor

 5
Author: Mauricio Scheffer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:29:15