Z FluentNHibernate+Autofac do CastleWindsor

0

hej,

do tej pory pracowałem w takiej konfiguracji:
Projekt 1: Class Library, w którym miałem klasy tabel, mapping, repozytoria oraz interface.
Projekt 2: ASP.NET MVC: w pliku Global robiłem Autofac mapowania Interfejs do repozytorium, potem wstrzykiwałem odpowiednie interfejsy do kontrolerów.

Teraz w firmie mi powiedzieli abym przestawił się na Castle Windsor.

Chciałbym aby ktoś mi pomógł z tym. Nie do końca rozumiem idee tego.
Rozumiem to tak:: dalej używam sobie NHibernate, dalej będę miał folder z klasami-tabelami, dalej będę miał folder Mappings (mapowanie tych tabel). Dalej będę miał repozytoria oraz interfejsy. Ale jak mam użyć tego Windsora zamiast Autofac ? Czy dalej będę wstrzykiwał interfejsy do kontrolerów? Jak to powinienem zrobić?

0

Windsor to kontener IoC tak samo jak Autofac. Usuwasz z projektu Autofaca, instalujesz Windsora, piszesz konfigurację i działa.

0

Nie wiemy jak dużo zaleznosci na stary kontener miała Twoja aplikacja.
Jeżeli w programie nie używałeś do wstrzykiwania jakichś atrybutów charakterystycznych dla danego kontenera IoC (kod w Twojej aplikacji jest niezależny od zastosowanego frameworka IoC), to wtedy jedyne co musisz zrobić
to:

  • usunąć z projektu zależność na stary kontener IoC (od razu wyjdzie, gdzie były zależności do tej biblioteki) i dodać nową biblioteke
  • zamienić konfiguracje mapowań interfejsów na klasy zgodnie z nową biblioteką, pamiętając o ustawieniu odpowiedniego cyklu życia (o ile jest to mozliwe i w nowym beda odpowiedniki tych samych cykli. Castle Windsor jest rozbudowany, wiec pewnie beda).
  • Podmienić implementację fabryki kontrolerów na wykorzystującą nowy kontener
  • Opcjonalnie, jak masz jakieś helpery/atrybuty/inne metody z zależnością na stary kontener to też je będziesz musiał przerobić.
0

@ne0 Po co chcesz zamieniac kontener IoC w jakims juz rozgrzebanym projekcie? Tamten przestal dzialac?

0

Nie chce zmieniać w istniejącym projekcie. Będę tworzył od nowa nowy projekt, i zamiast Autofac-a mam użyć windsora i chciałbym aby któryś z was mi z tym pomógł.
Tak robiłem do tej pory:
Projekt Class Library:
klasa Samochód:

public class Samochod
{
public virtual int Id {get; set;}
public virtual string Name {get; set;}
}

klasa map:

public class SamochodMap: ClassMap<Samochod>
{
public SamochodMap()
{
this.Id(x=>x.Id);
this.Map(x=>x.Name);
}
}

potem jakieś repo oraz interfejs, nastepnie w projekcie aplikacji ASP.NET MVC w global ustawiałem konfiguracje i byłam tam taka linijka mniej więcej:

            builder.RegisterType<SamochodRepository>().As<ISamochodRepository>();

I chciałbym zobaczyć przykład jak to powinno wyglądać z windsorem

0

Ale przeciez nHibernate i Twoje obiekty biznesowe nie maja nic do kontenera i nie powinny miec.
Twoje pytanie to de facto pytanie o to jak rejestrowac z uzyciem castle windsor. Na necie jest multum przykladow.

container.Register(
    Component.For<IMyService>()
        .ImplementedBy<MyServiceImpl>()
);
0

no tak ale gdzie to powinno się znaleźć ? też w global w projekcie ?

2

Ma sie wykonac przy starcie aplikacji, wiec moze byc np. w globalu.

0

Zrobiłem prostą klasę, do tego interfejs, repozytorium.
Nastepnie w pliku global zrobiłem:


        private static void StartWindsor()
        {
           var container = new WindsorContainer();
            container.Register(Component.For<ICarRepository>().ImplementedBy<CarRepository>());
        }

o to właśnie chodzi?

1

Skrobnalem ci przyklad: https://github.com/vixlur/WindsorExample
#W Infrastructure masz 2 pliki: WindsorBootstrapper i WindsorDependencyResolver
#W HomeController wstrzykujesz implementacje IMessageProvider
#W Message i ApplicationContext masz prosty model i prosta implementacje tego interfejsu powyzej.
#W Global tylko wywolujesz metode Initialize z WindsorBootrapper i potem w Application_End zwalniasz kontener.

0

Dla zrozumienia:

  1. WindsorBootstrapper:
 private static void AddBindings(IWindsorContainer container)
    {
      container.Register(Component.For<IMessageProvider>().ImplementedBy<ApplicationDbContext>().LifestylePerWebRequest());
    }

tutaj ustawiam interfejsy i ich implementacje. musze takie cos napisac dla kazdego uzywanego interfejsu- tak?

  1. Po co jest ten kod:
 private static void InstallControllers(IWindsorContainer container)
    {
      container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
    }
  1. nie rozumiem po co i co robi kod z pliku WindsorDependencyResolver- czy mozesz mi to wytlumaczyc?
0

musze takie cos napisac dla kazdego uzywanego interfejsu- tak?

Niekoniecznie, click

Po co jest ten kod:

Zeby dodac do kontenera wszystkie kontrolery, inaczej dostaniemy cos w tym stylu: No parameterless constructor defined for this object.

  1. nie rozumiem po co i co robi kod z pliku WindsorDependencyResolver- czy mozesz mi to wytlumaczyc?

Implementuje IDependencyResolver, ktory bedzie pozniej uzywany przez mvc do wyciagania obiektow danego typu. Dlatego tez rejestrujemy kontrolery, zeby sobie lezaly w kontenerze i mogl spokojnie framework je wyciagnac.

0

A powiedz mi teraz tak: zalozmy ze jeden projekt to bedziemy WebAPI, drugi projekt strona w MVC i trzeci projekt to mialbym repozytoria, interfejsy i mappingi - gdzie powinny byc po kolei te pliki ktore stworzyles?

Tak BTW czy Tobie ten kod sie kompiluje? bo ja juz ktorys raz to probuje odpalic i nie dziala. W WindsorDependencyResolver dostaje blad ze nie mam zaimplementowanych: CanResolve, RemoveSubResolver, Initialize, AddSubResolver oraz Resolve: problem jest zarowno w System.Web.Mvc.IDependencyResolver jak i Castle.MicroKernel.IDependencyResolver.

Mam tu jeszcze pytanie do tego:

private static void AddBindings(IWindsorContainer container)
    {
      container.Register(Component.For<IMessageProvider>().ImplementedBy<ApplicationDbContext>().LifestylePerWebRequest());
    }

rozumiem ze jak mam ICarRepository i CarRepository to bedzie to tak wygladac:

private static void AddBindings(IWindsorContainer container)
    {
      container.Register(Component.For<ICarRepository>().ImplementedBy<CarRepository>().LifestylePerWebRequest());
    }

?

0

A powiedz mi teraz tak: zalozmy ze jeden projekt to bedziemy WebAPI, drugi projekt strona w MVC i trzeci projekt to mialbym repozytoria, interfejsy i mappingi - gdzie powinny byc po kolei te pliki ktore stworzyles?

Zrob 4 projekt tylko do ioca albo ioca i czegos tam jeszcze co ci pozniej wpadnie do glowy. W niego wrzuc DependencyResolver i Bootstrapper. W projekcie mvc i web api, robisz sobie implementacje interfejsu IWindsorInstaller, ktora bedzie instalowac odpowiednie kontrolery i tyle. Jak uzywac installerow znajdziesz tu: http://docs.castleproject.org/Windsor.Installers.ashx

Tak BTW czy Tobie ten kod sie kompiluje?

Tak, kompiluje sie. [visual studio 2013 express for web/windows 7 pro x64]. Na pewno kliknales w nugetcie restore?

rozumiem ze jak mam ICarRepository i CarRepository to bedzie to tak wygladac:

Tak, ale ten LifeStyle to niekoniecznie musi taki byc.

0
n0name_l napisał(a):

Zrob 4 projekt tylko do ioca albo ioca i czegos tam jeszcze co ci pozniej wpadnie do glowy. W niego wrzuc DependencyResolver i Bootstrapper. W projekcie mvc i web api, robisz sobie implementacje interfejsu IWindsorInstaller, ktora bedzie instalowac odpowiednie kontrolery i tyle. Jak uzywac installerow znajdziesz tu: http://docs.castleproject.org/Windsor.Installers.ashx

A co daje oddzielny projekt do IoC i jeszcze jakichś śmieci? Czemu nie rejestrować modułów IoC ze wszystkich projektów w aplikacji?

0

Jak moge polaczyc ten Windsora z NHibernate?
Bo mam zrobione to niby tak jak powinno byc, ale wywala mi blad: "Can't create component 'Infrastructure.Repositories.CarRepository' as it has dependencies to be satisfied.".
Kod:
WindsorBootstrapper:


  private static void AddBindings(IWindsorContainer container)
        {
            container.Register(Component.For<ICarRepository>().ImplementedBy<CarRepository>().LifestylePerWebRequest());
            container.Register(Component.For<IPersonRepository>().ImplementedBy<PersonRepository>().LifestylePerWebRequest());
            container.Register(Component.For<ISessionFactory>().UsingFactoryMethod(CreateSess).LifeStyle.Singleton); 
        }

  private static ISessionFactory CreateSess()
        {
            const string ConnString = @"Data Source=KN-Komp-01\SQLEXPRESS;Initial Catalog=Test;Integrated Security=True";
            var factory = Fluently.Configure()
                //.Database(MsSqlConfiguration.MsSql2012.ConnectionString(c => c.FromConnectionStringWithKey("default")))
                     .Database(MsSqlConfiguration.MsSql2012.ConnectionString(ConnString))
                //.ExposeConfiguration(x => x.SetInterceptor(new SqlStatementInterceptor()))
                     .Mappings(m => m.FluentMappings.AddFromAssemblyOf<CarMap>())
                     .BuildSessionFactory();
            return factory;

        }

carRepository:

 public class CarRepository: ICarRepository
    {
        private readonly ISession session;

        public CarRepository(ISession session)
        {
            this.session = session;
        }      

        public IEnumerable<Car> ListCars()
        {
            return this.session.Query<Car>();
        }

//EDIT:
chyba udalo mi sie to zrobic bo wyglada ze dziala poprawnie :)

0

Teraz pytanie mam o konwencje. WindsorBootstrapper wygląda teraz tak:

(...)
      private static void InstallControllers(IWindsorContainer container)
        {
            container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
        }

        private static void AddBindings(IWindsorContainer container)
        {
            

            container.Register(Component.For<ICarRepository>().ImplementedBy<CarRepository>().LifestylePerWebRequest());
            container.Register(Component.For<IPersonRepository>().ImplementedBy<PersonRepository>().LifestylePerWebRequest());
            container.Register(Component.For<ISessionFactory>().UsingFactoryMethod(CreateSess).LifeStyle.Singleton); 
            container.Register(Component.For<ISession>().UsingFactoryMethod(kernel => kernel.Resolve<ISessionFactory>().OpenSession()));
        }

(...)

Rozumiem że dzięki zastosowaniu konwencji, nie musiałbym mieć tych dwóch linijek zawierających ICarRepository i IPersonRepository - zgadza się?
Jeśli tak to jak powinien wyglądać kod? Czy ten jest poprawny: (niby się kompiluje ale HomeController do którego wstrzykuje te dwa Repo się wysypuje ponieważ nie są wstrzykiwane jednak)

container.Register(Classes.FromThisAssembly()
                    .InSameNamespaceAs<ICarRepository>()
                    .WithService.DefaultInterfaces()
                    .LifestyleTransient());

EDIT:
jeszcze mam jedno pytanie: gdzie powinienem wsadzić całą tą konfigurację? Mam w projekcie: strone www (asp.net mvc) i do tego 3 class library: dataModel, Repos i Services.
To gdzie powinna być ta konfiguracja? W projekcie MVC ?

0

Masz w drugim przypadku inny cykl zycia (transient zamiast per web request).
Konfiguracje wywoluj w Global.asax.cs

0

okay, to już wsadziłem do Global i działa, ale teraz chciałbym zastosować te tak zwane konwencje, mógłbyś mi to pokazać?

1 użytkowników online, w tym zalogowanych: 0, gości: 1