Schlagwort-Archive: CastleWindsor

Webcast Strategy Pattern mit IoC

Den Webcast zu diesem Blogeintrag gibt es hier.

Advertisements

Strategy Pattern mit IoC sauber implementiert

Ein kurzer Hinweis vorab: Den Inhalt des Blogeintrags habe ich auch in einem Webcast zusammengefasst. Das Video gibt es auf YouTube. Den Code findet ihr hier.

In meinem letzten Blogeintrag habe ich das Strategy Pattern in der Praxis gezeigt. Primär gab es noch einen Smell, welchen ich erwähnt hatte: Das Open Closed Principle ist damit noch nicht 100%ig eingehalten und an einer Stelle heble ich mein IoC Konzept aus. Das passiert genau hier:

   1: public StateHandler(IClock clock, IBuildDefaultProposalsValues defaultValues)

   2: {

   3:     Handler = new Dictionary<SelloutStates, ICreateOrderProposal>

   4:               {

   5:                   {SelloutStates.Oversupply, null},

   6:                   {SelloutStates.Predicted, new PredictedGap(defaultValues)},

   7:                   {SelloutStates.Urgent, new UrgentGap(defaultValues, clock)},

   8:                   {SelloutStates.AppointmentWarnings, new AppointmentWarning(defaultValues, clock)},

   9:                   {SelloutStates.Unknown, null},

  10:                   {SelloutStates.Verify, new NoForecastPossible(defaultValues)},

  11:                   {SelloutStates.Ok, new UncomplicatedAppointment(defaultValues, clock)}

  12:               };

  13: }

Zum einen müsste ich die Klasse immer anpassen, wenn ich einen weiteren Status aufnehmen möchte. Zum anderen erzeuge ich mir Instanzen von Klassen, welche ich nicht über den Container erhalten habe, sprich ich hole mir die Abhängigkeiten selbst, statt mir diese gemäß Inversion of Control Principle zuweisen zu lassen. Das wäre zwar möglich gewesen, allerdings würde der Konstruktor dadurch erheblich aufblähen, weil ich pro Status eine Implementierung reinreichen müssten. Dieses Problem könnte ich über Service Location lösen, d.h. ich lasse mir lediglich den Container reinreichen und mache die Auflösung intern. Für mich persönlich auch keine schöne Lösung (Hinweis: Dafür müsste man den Container in sich selbst registrieren…).

Ob nun saubere Dependency Injection mit aufgeblähtem Konstruktor oder Service Location, in jedem Fall bliebe das Problem, dass ich jedes Mal im Container eine weitere Registrierung für einen neuen Status durchführen müsste.

Daraufhin kamen Rückfragen, wie dies am besten zu lösen sei. Das soll Thema dieses Blogeintrags und des dazu passenden Webcasts sein, v.a. weil im Netz auch nur selten saubere Implementierungen anzutreffen sind, die alle Prinzipien einhalten.

Als erstes wird der obere Code gänzlich entfernt, da dieser nicht mehr benötigt wird. Hinter dem Code steckte eigentlich das Factory Pattern. Ich lasse mir durch die Factory (abhängig vom Status) das entsprechende Objekt erzeugen und arbeite dann mit diesem weiter. Castle.Windsor als IoC Container bietet uns hierfür sogenannte interface-based factories an.

Anmerkung: Der folgende Code basiert nicht mehr auf dem bisherigen, welchen ich im vorherigen Blogeintrag verwendet habe. Im Webcast ist dazu alles erläutert und ich habe das Projekt unter Github zur Verfügung gestellt.

   1: yield return Component

   2:     .For<IchVermittleVorschlagsrechner>()

   3:     .AsFactory(c => c.SelectedWith(new VorschlagsrechnerVermittler()));

Wie man sieht, muss man lediglich das Factory-Interface, welches man zuvor selbst ausimplementiert hatte, angeben und im Anschluss ‘AsFactory’ anhängen. Als Parameter übergebe ich den Verweis auf eine Logik, welche die Factory später intern verwendet, um mir das korrekte Objekt (den korrekten Service) zurückzuliefern. Das Bedeutet, dass “new VorschlagsrechnerVermittler” der Factory, wenn auf ihr ein Aufruf stattfindet, sagen muss, was sie zurückgeben soll. Es folgt die Implementierung:

   1: public class VorschlagsrechnerVermittler : DefaultTypedFactoryComponentSelector

   2: {

   3:     protected override string GetComponentName(MethodInfo method, object[] arguments)

   4:     {

   5:         if (method.Name == "Für" && arguments.Length == 1 && arguments[0] is Bestandsstatus)

   6:         {

   7:             var status = arguments[0].ToString();

   8:             //von mir frei gewählte Konvention wie die Komponente im Container heißt

   9:             return string.Format("BerechnerFürArtikelstatus{0}", status);

  10:         }

  11:         return base.GetComponentName(method, arguments);

  12:     }

  13: }

Dieser Code macht nun genau das, was ich vorher explizit im Dictionary definiert habe, allerdings anhand einer Konvention. Die Methode in Zeile 3 liefert als String den Namen der Komponente zurück basieren auf dem Namen des Status, also z.B. ergibt sich für den Status “Ok” der Komponentenname “BerechnerFürArtikelstatusOk”.

Nun fehlt lediglich noch eine kleine Anpassung für den IoC Container. Dieser weiß bisher nichts von einer Komponente “BerechnerFürArtikelstatusOk”. Eine Möglichkeit wäre, dass ich – wie bereits eingangs erwähnt – für jeden Status die Ausprägung im Container registriere, also in diesem Fall beispielsweise so:

   1: yield return Component.For<IchErrechneArtikelZukaufsvorschlag>()

   2:     .ImplementedBy<Ok>()

   3:     .Named("BerechnerFürArtikelstatusOk")

   4:     .LifestyleTransient();

Das widerspricht immer noch dem OCP, sodass ich es wie folgt löse:

   1: yield return AllTypes.FromThisAssembly()

   2:     .BasedOn<IchErrechneArtikelZukaufsvorschlag>()

   3:     .Configure(a => a.Named(string.Format("BerechnerFürArtikelstatus{0}",

   4:         a.Implementation.UnderlyingSystemType.Name)))

   5:     .Configure(a => a.LifestyleTransient());

Zunächst hole ich mir alle Klassen aus der aktuellen Assembly, welche auf “IchErrechneArtikelZukaufsvorschlag” basieren, also alle meinen konkreten Status-Implementierungen. Im Anschluss sage ich noch, welchen Name diese Komponenten im Container bekommen sollen. Hier steht die selbe Konvention wie in dem Code Sample  zuvor. Der Vollständigkeit halber sei erwähnt, dass ich hier gegen das Don’t Repeat Yourself Principle verstoße. Dies soll lediglich der Vereinfachung im Beispiel dienen.

Ab jetzt muss ich, wenn ein weiterer Status hinzu kommt, lediglich eine Implementierung für diesen machen. Bestehender Code muss nicht mehr angefasst werden.

Universal Service Host

Ich habe soeben in meinem Github Repository ein Projekt zur Verfügung gestellt, mit welchem ihr euch sehr einfach NT Dienste basteln könnt. Einfach das Interface “IAmService” aus der Contracts Assembly in eurem Projekt implementieren und mit Castle.Windsor das Ganze im IoC-Container registrieren und schon könnt ihr euer Programm als Dienst bereitstellen. Das geht im Übrigen auch für eine beliebige Anzahl an Diensten, solange die DLLs dazu im gleichen Verzeichnis wie die EXE-Datei liegen.

Hier ein Video, in dem ich alles erkläre:

Entity Framework Webcasts – UnitOfWork und IoC

 

Ich habe auf YouTube mit einer Webcast Reihe zum Entity Framework begonnen. Ich stelle dabei einen Lösungsansatz vor, der zum Ziel hat das EF in der Anwendungsarchitektur als Persistenzschicht derart zu integrieren, dass Unit und Integration Tests einfach zu implementieren sind. Darüber hinaus soll der Ansatz gängige Prinzipien wie Inversion of Control (IoC), Single Responsibility Principle (SRP), Open-Closed-Principle (OCP) umsetzen, sowie die Abhängigkeiten zum Framework minimieren.

In diesem zweiten Teil zeige ich wie man durch den Einsatz eines IoC-Containers eine direkte Abhängigkeit zur Persistenzschicht vermeiden kann. Darüber hinaus setze ich hier bereits das Konzept einer UnitOfWork ein, welche ich in einem folgenden Teil näher erläutern werde.

Quellen:

Castle Windsor – Handy Ruse Part III

In this blog entry I published code for an IoC Initializer. The following container Integration Test tries to create an instance of each registered service that is not of type IAmNotTestable. Definitely an Integration Tests that every application needs!

Integration Test of the container using Machine.Specifications as UnitTesting Framework:

   1: using System;

   2: using System.Diagnostics;

   3: using System.Linq;

   4: using Castle.MicroKernel;

   5: using Castle.Windsor;

   6: using comWORK.Contracts;

   7: using comWORK.Infrastructure.IoC;

   8: using Machine.Specifications;

   9: using Machine.Specifications.Utility;

  10:  

  11: namespace UAR.IntegrationTests

  12: {

  13:     public abstract class ContainerSpecs

  14:     {

  15:         protected static IWindsorContainer CreateContainer()

  16:         {

  17:             return new IoCInitializer()

  18:                     .RegisterComponents()

  19:                     .Container;

  20:         }

  21:     }

  22:  

  23:     [Subject("Container Specs")]

  24:     public class When_the_application_starts_up : ContainerSpecs

  25:     {

  26:         static IHandler[] _handlers;

  27:         static IWindsorContainer _container;

  28:  

  29:         Establish context = () => { _container = CreateContainer(); };

  30:  

  31:         Because of = () => { _handlers = _container.Kernel.GetAssignableHandlers(typeof(object)); };

  32:  

  33:         Cleanup after = () => _container.Dispose();

  34:  

  35:         It should_be_able_to_create_an_instance_of_each_registered_service =

  36:             () => _handlers

  37:                       .Where(

  38:                           handler =>

  39:                           handler.ComponentModel.Implementation.GetInterfaces().Contains(typeof(IAmNotTestable)) == false)

  40:                       .Each(handler => handler.ComponentModel.Services

  41:                                            .Each(service =>

  42:                                            {

  43:                                                Debug.WriteLine(String.Format("{0}: {1}/{2}",

  44:                                                                              handler.ComponentModel.Name,

  45:                                                                              service.Name,

  46:                                                                              handler.ComponentModel.Implementation.Name));

  47:  

  48:                                                if (service.ContainsGenericParameters)

  49:                                                {

  50:                                                    service.GetGenericArguments()

  51:                                                        .Each(argument => argument.GetGenericParameterConstraints()

  52:                                                                              .Each(

  53:                                                                                  constraint =>

  54:                                                                                  _container.Resolve(service.MakeGenericType(constraint))));

  55:                                                }

  56:                                                else

  57:                                                {

  58:                                                    _container.Resolve(service);

  59:                                                }

  60:                                            }));

  61:     }

  62: }

Implementation of IAmNotTestable

   1: public interface IAmNotTestable{}

Notice: I will publish a new version soon that addresses some open problems that i don’t want to mention here.

Castle Windsor – Handy Ruse Part II

In every application you have to initialize the container as part of the bootstrapping process. I moved the bootstrapping logic into a seperate assembly. Here’s the IoC part as fluent API. The code registers all implementations of IWindsorInstaller contained in the folder of the executing assembly. I also have a method “RunStartupConfiguration” that executes the start-method of all components which implement the interface. This could be useful for configuration reason for example.

Implementation of IoCInitializer

   1: public class IoCInitializer : IDisposable

   2: {

   3:     public IWindsorContainer Container { get; private set; }

   4:  

   5:     public IoCInitializer()

   6:     {

   7:         Container = new WindsorContainer();

   8:     }

   9:  

  10:     public void Dispose()

  11:     {

  12:         if (Container != null)

  13:         {

  14:             Container.Dispose();

  15:             Container = null;

  16:         }

  17:     }

  18:  

  19:     public IoCInitializer RegisterComponents()

  20:     {

  21:         var appDomainDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

  22:         var foundAssemblies = FromAssembly.InDirectory(new AssemblyFilter(appDomainDirectory));

  23:  

  24:         Container.Install(foundAssemblies);

  25:  

  26:         return this;

  27:     }

  28:  

  29:     public IoCInitializer RunStartupConfiguration()

  30:     {

  31:         Container

  32:             .ResolveAll<IStartupTask>()

  33:             .ToList()

  34:             .ForEach(x => x.Start());

  35:         return this;

  36:     }

  37: }

 

Implementation of IStartupTask:

   1: public interface IStartupTask

   2: {

   3:     void Start();

   4: }

 

You can call this in your bootstrapping logic this way:

   1: Container = new IoC.IoCInitializer()

   2:     .RegisterComponents()

   3:     .RunStartupConfiguration()

   4:     .Container;

 

Be careful, the container and class modifiers are public! Instead you should make it internal and integrate it into the assembly that is responsible for bootstrapping.

Castle Windsor – Handy Ruse Part I

Today I want to share some implementations that I use every once in a while:

Scenario 1: You have an assembly that contains all WinForm controls or perhaps ViewModel classes instead. You don’t want to adapt your installer again and again, if you create new controls respectively new ViewModels.

The following snippet shows an Installer that registers all ViewModels in a WPF assembly of mine. Of course I need to decorate my ViewModels with an special interface, in this case IAmViewModel:

   1: public class Installer : IWindsorInstaller

   2: {

   3:     public void Install(IWindsorContainer container, IConfigurationStore store)

   4:     {

   5:         container.Register(Components().ToArray());

   6:     }

   7:  

   8:     private static IEnumerable<IRegistration> Components()

   9:     {

  10:         yield return Classes.FromThisAssembly()

  11:             .BasedOn<IAmViewModel>()

  12:             .WithServiceSelf()

  13:             .LifestyleTransient();

  14:     }

  15: }

You can afford the same for WinForms. You don’t even have to create an interface:

   1: private static IEnumerable<IRegistration> Components()

   2: {

   3:     yield return Classes.FromThisAssembly()

   4:         .BasedOn<Form>()

   5:         .WithServiceSelf()

   6:         .LifestyleTransient();

   7: }

 

In some little (!) cases it can be useful to inject the container itself into an object. For that reason you can register the container in itself:

   1: Container.Register(

   2:     Component.For<IWindsorContainer>().Instance(Container)

   3: );

%d Bloggern gefällt das: