Posts Tagged ‘Dependency Injection’

StructureMap: Wybieranie, który konstruktor ma zostać użyty

Author: Mateusz Kubiczek (madmatt) | wrzesień 16th, 2009

StructureMap to najstarszy framework dependency injection dla .NET. Jak łatwo się domyślić, używamy go w celu implementowania wstrzykiwania zależności (artykuł Martina Fowlera ).

Pewien czas temu chciałem użyć go wraz z WCF. Dla webserwisu ServerService WCF wygenerował interfejs IServerServicePortType oraz klienta ServerServicePortTypeClient. Konfiguracja tego webserwisu znajdowała się w pliku Web.config.

Zdefiniowałem regułę dla StructureMap:

ObjectFactory.Configure( x =>
{
   x.ForRequestedType<IServerServicePortType>().TheDefault
     .Is.OfConcreteType<ServerServicePortTypeClient>();
});

która powinna spowodować, że pobranie za pomocą ObjectFactory.GetInstance<IServerServicePortType>() zwróci nam obiekt klienta.

Wszystko fajnie, tylko że StructureMap wybierał zły konstruktor tej klasy. Jako że konfiguracja znajdowała się w pliku Web.config, chciałem, żeby StructureMap tworzył klasę za pomocą konstruktora bezparametrowego – niestety próbował użyć innego, co powodowało błąd (brak argumentów dla tego konstruktora). Spójrzmy na wygenerowane konstruktory klasy ServerServicePortTypeClient:

public ServerServicePortTypeClient();
public ServerServicePortTypeClient(string endpointConfigurationName);
public ServerServicePortTypeClient(string endpointConfigurationName, string remoteAddress);
public ServerServicePortTypeClient(string endpointConfigurationName,
                             System.ServiceModel.EndpointAddress remoteAddress);
public ServerServicePortTypeClient(System.ServiceModel.Channels.Binding binding);

Wykorzystywany był drugi z argumentem endpointConfigurationName.

Nie tego oczekiwałem. Logiczne było, że zostanie wybrany konstruktor najlepiej pasujący do podanych argumentów – czyli brak argumentów spowoduje wybranie bezargumentowego. :-) Po pewnym czasie znalazłem rozwiązanie na blogu autora StructureMap.

ObjectFactory.Configure( x =>
{
   x.SelectConstructor( () => new SoapPortTypeClient());
   x.ForRequestedType().TheDefault.Is.OfConcreteType();
});

Pierwsze wyrażenie jest co najmniej ciekawe – nie tworzymy tutaj obiektu, tylko definiujemy lambdę, z której następne StructureMap odczyta, jakiego konstruktora powinien uzyć. Sprytne!