Castle Windsor - warunkowe wstrzykiwanie zależności

0

Mam sobie solucję, która zawiera między innymi dwa projekty.

  1. Backend - logika biznesowa, context DB (nazwijmy go DBContextBE) dziedziczący po DbContext z którego korzysta logika biznesowa.
  2. API - tutaj znajduje się drugi context DB (nazwijmy go DBContextAPI) dziedziczący po DbContext z którego korzystają niektóre metody w tym projekcie.

Natomiast po stronie backendu (projekt punkt 1) jest adapter EF zawierający metody zapisu, aktualizacji czy tworzenia obiektów modeli bazy danych.
Po stronie API część metod z kontrolera odwołuje się do lokalnej logiki i DBContextAPI, a część metod do logiki z backendu (punkt 1) gdzie potrzebny jest DBContextBE.
I tutaj mam problem. Jak zarejestrować po stronie API dany context DB w zależności od tego, która logika jest w danym momencie używana?
Np. odpalam metodę z kontrolera API, która korzysta z logiki na backendzie gdzie operujemy na DBContextBE i gdy jest używana metoda z klas backendowych to rejestruję DBContextBE gdyż w konstruktorze adaptera EF oczekiwany jest obiekt klasy DBContext.

public void Install(IWindsorContainer container, IConfigurationStore store)
        { 
            container.Register(Component.For<IDbContextAdapter>()
                .ImplementedBy<EfDbContextAdapter>()
                .LifestyleTransient());

            //Odpalam metodę wykorzystującą logikę z backendu to do EfDbContextAdapter potrzebuję wstrzyknąć DBContextBE
            container.Register(Component.For<DbContext>()
                .ImplementedBy<DBContextBE>()
                .LifestyleTransient());

             //Odpalam metodę wykorzystującą logikę z API to do EfDbContextAdapter potrzebuję wstrzyknąć DBContextAPI
            container.Register(Component.For<DbContext>()
                .ImplementedBy<DBContextAPI>()
                .LifestyleTransient());
}

Jak to uwarunkować? A w zasadzie jak poznać na etapie rejestracji, że w danym momencie jest uruchamiana dana metoda?

2

Zrób fabrykę, która będzie zwracała odpowiedni obiekt, w zależności od warunków
.Albo lepiej użyj różnych typów, a nie jednego.

0

Taka fabryka oparta na swich'u zazwyczaj łamie open/closed principle.

Ja bym wolał zarejestrować implementacje IDictionary, która by działała jak IIndex z Autofac'a.

Potem robisz sobie kompozyt, który wybiera kontekst na podstawie argumentu z dictionary.

2
Gworys napisał(a):

Potem robisz sobie kompozyt, który wybiera kontekst na podstawie argumentu z dictionary.

Czyli fabrykę.

0

Panowie nie wiem co tu się odpikoliło, ale generalnie jeszcze średnio łapię Wasze specjalistyczne określenia. Najlepiej jak byście mogli mi podrzucić jakiś przykład.
Co nieco już programuje i sporo rzeczy mi się udało zrobić, głównie bazując na przykładach ze stacka czy innych, ale nie są mi znane jeszcze wszystkie zagadnienia, które mógłbym z kimś przedyskutować face tu face. Dlatego też chyba potrzebuję aby ktoś mi wytłumaczył jak krowie na rowie.
Co to znaczy zrobić fabrykę zwracającą odpowiedni obiekt?
Ta fabryka będzie operowała rejestracją w windsorze?
I teraz nie bardzo wiem jak sprawdzić z poziomu windsora, który context jest używany.

2

Możesz sobie napisać klasę jak ta:

public class DbContextProvider
{
    private DbContext dbContextApi, dbContextBe;

    public DbContextProvider(DBContextAPI dbContextApi, DBContextBE dbContextBe)
    {
        this.dbContextApi = dbContextApi;
        this.dbContextBe = dbContextBe;
    }   

    public DbContext CreateContext()
    {
        if(warunek API)
            return this.dbContextApi;
        else if (warunek BE)
            return this.dbContextBe;

        throw new Exception();
    }
}

Potem ją skonfigurować w Windsorze i wstrzykiwać do klas potrzebujących DbContextu.

Ale czemu nie wstrzykiwać DBContextAPI i DBContextBE tam gdzie potrzebne, zamiast oba konfigurować jako DBContext?

0

Nie ma żadnego profitu z tego, że wstrzykujesz te konteksty do tego prvaidera, mniejsza z tym, że jest to słaba nazwa.

Poza tym łamiesz OCP każda próba rozszerzenia klasy "provaidera" będzie się łączyła dopisywaniem kolejnego ifa i dopisywania nowego parametru do konstruktora.

Opcja Gworysa jest o wiele lepsza. Mało tego też bym się wnerwił na jego miejscu.

I niby czym się różni ta fabryka na ifach od tej na switchu.?

0

to pokaż jak byś to zrobił zgodnie z koncepcją Gworysa. Wtedy każdy będzie mógł popatrzeć na dwie implementacje i sam zdecydować co jest lepsze i w jakiej sytuacji :)

Domyślam się, że chodzi w uproszczeniu mniejwięcej o coś takiego:

//contener IOC
builder.RegisterType<Context<DbAPI>>().Keyed("arg1");
builder.RegisterType<Context<DbBE>>().Keyed("arg2");

//Gdzieś w logice
Dictionary.Key("arg1").Context()

No i....

0

No to, że Ty na myśl fabryka myślisz switch albo service locator to już nie jest nasz problem, nie jest to też problem rozwiązywalny na forum programistycznym.

Moim zdaniem to, co ktoś sobie myśli na myśl Fabryka to nie istotne, bo to tylko wysokopoziomowy interfejs za fabryką może być cokolwiek. Jakaś fasada, service locator, whatever...

@somekind To mistrz wykorzystywania homonimy. Prowadzi debaty z poziomem co najmniej dzisiejszych liderów PO, lecz wiedzy technicznej dalej mu brakuje...

0
Mimik59 napisał(a):

Nie ma żadnego profitu z tego, że wstrzykujesz te konteksty do tego prvaidera, mniejsza z tym, że jest to słaba nazwa.

Pewnie, że nie ma. Dlatego napisałem, że tak można zrobić, ale żeby tego nie robić.

Poza tym łamiesz OCP każda próba rozszerzenia klasy "provaidera" będzie się łączyła dopisywaniem kolejnego ifa i dopisywania nowego parametru do konstruktora.

Owszem. Jeśli komuś nie podoba się łamanie OCP, to zawsze może sobie napisać mądrzej:

public class DbContextProvider
{
    private CleverDbContext[] contexts;

    public DbContextProvider(CleverDbContext[] contexts)
    {
        this.contexts= contexts;
    }   

    public CleverDbContext CreateContext()
    {
        foreach (var ctx in this.contexts)
             if (ctx.CanBeUsed(/*pewnie jakieś parametry*/)
                 return ctx;

        throw new Exception();
    }
}

Ale to dalsze bredzenie w overengineering, podczas gdy tak naprawdę rozwiązywany jest tu chyba problem X/Y, a prawidłowym i działającym rozwiązaniem jest niebindowanie tych kontekstów do klasy bazowej, bo skoro nie są używane zamiennie, to żadnego pożytku z takiej konfiguracji nie ma.

I niby czym się różni ta fabryka na ifach od tej na switchu.?

No generalnie tym, że w ifach można mieć wyrażenia logiczne, a w switchu nie. (Chyba jednak usunięcie Newbie było złym pomysłem, skoro takie pytania się pojawiają.)

Domyślam się, że chodzi w uproszczeniu mniejwięcej o coś takiego:

//contener IOC
builder.RegisterType<Context<DbAPI>>().Keyed("arg1");
builder.RegisterType<Context<DbBE>>().Keyed("arg2");

//Gdzieś w logice
Dictionary.Key("arg1").Context()

No tak, magiczne stringi są tak bardzo lepsze niż ify. :)

Opcja Gworysa jest o wiele lepsza. Mało tego też bym się wnerwił na jego miejscu.

Ja na jego miejscu też bym się wkurzył, że ktoś pisze z jego komputera i twierdzi, że nie jest nim.

PS, dużo jest Was tam, w środku?

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