Schlagwort-Archive: IoC

Die großen 4: Pfadfinderregel, Wirtschaftlichkeit, Clean Code, SOLID Principles

Was ist damit gemeint? Gemäß der Pfadfinderregel soll ein Entwickler Code immer besser hinterlassen, als er ihn vorgefunden hat. Clean Code oder guter Code ist häufig dann erreicht, wenn das Mindestmaß an essentiellen Code Prinzipien umgesetzt ist. Das sind die sogenannten SOLID Principles. Jedoch ist guter Code kein Selbstzweck, sondern dient dem größeren Ziel der Wirtschaftlichkeit.

Im folgenden Video zeige ich an einem Praxisbeispiel, wie ich bei einem bestehendem, eher unwichtigem Projekt vorgegangen bin. Timeboxed in 1h so viel refaktorisieren und den Code verbessern wie möglich. Dabei gehe ich auf Prinzipien wie DRY und OCP ein und zeige Techniken wie DI, sowie Tools wie den IoC Container Castle Windsor.

Feedback nehme ich wie immer gerne mit. Wenn ihr mehr von solchen Videos sehen wollt, schreibt mir das in die Kommentare, damit ich weiß: Hier lohnt es sich mehr zu machen.

Zum YouTube Video

Durch Klicken auf das Bild geht es zum Video

Werbung

Lazy Loading mit IoC

Update vom 04.09.2013: Beachtet bitte die Kommentare. Das Lazy-Feature kann auch erhebliche Performance-Einbußen zur Folge haben. Es kommt darauf, was der Konstruktur des Lazy-Objekts noch an Abhängigkeiten hat und welche Logik in selbigem noch ausgeführt wird.

In diesem Webcast zeige ich welche Möglichkeiten Castle.Windsor für das Lazy Loading von Abhängigkeiten bietet. Das Feature wurde in Version 3 eingeführt. Weiterführenden Inhalt welche Performance Gedanken dahinter stecken und wann idealerweise der Graph des Container geladen werden sollte, findet ihr hier und hier.

Für diejenigen, die einen anderen Container nutzen: Falls euer Framework die Funktionalität nicht out-of-the-box mitliefert, so stellt sich eine eigene Lösung nicht als allzu schwierig heraus. In dem oben zuletzt erwähnten Link gibt es dazu ein Code Sample.

Als grobe Richtlinie kann man sich merken:

Das Laden von weiteren Abhängigkeiten wird v.a. dann “teuer”, wenn die Implementierungen in Assemblies liegen, die noch nicht geladen wurden.

Castle Windsor Dependencies Tracking

Deployments für Anwendungen zu erstellen, welche modular über einen IoC Container wie Castle.Windsor aufgebaut sind, ist nicht ganz simpel. Zumindest ging es mir so, als ich vor der Herausforderung stand eine Konsolenanwendung zu deployen, welche im Einstiegspunkt eine Komponente auflöst und darauf eine Methode aufruft, die den gesamten Bearbeitungsprozess startet. Alle weiteren Abhängigkeiten werden dann über den internen Graphen des Containers aufgelöst. Findet er eine Komponente nicht, kommt es zur Exception:

image

Ich halte es in meinen Projekten so, dass die Kontrakte immer in einer separaten Assembly liegen. Als Namenskonvention erhält die DLL ein “.Contracts”. Ein Beispiel:

  • UAR.Demo //Implementierung
  • UAR.Demo.Contracts //Kontrakte

So weit wie möglich sind dann in der Implementierung alle Klassen internal oder private. Über einen öffentlichen Installer wird beim Start der Anwendung die Registrierung der angebotenen Kontrakte samt derer Implementierungen im Container vorgenommen.

Da demzufolge keinerlei Referenzen mehr zwischen den Implementierungen bestehen und ich in der oben erwähnten Konsolenanwendung nun nur genau einen Einstiegspunkt habe, der sich die initiale Komponente auflöst und startet, fehlen weitere benötigte Assemblies.

Als ich über Twitter das Castle Team diesbezüglich angesprochen habe, kam folgende Antwort:

it might not be trivial. You can walk the graph, but better option would be to have clean layering so it’s obvious

Leider half mir die Aussage nicht weiter, da ich keinen „clean layering”-Ansatz sehe, der besagtes Problem auflöst. Vielleicht kann mir einer der Leser weiterhelfen?

Als Lösung habe ich mich in das Event der Komponentenerstellung von Castle.Windsor gehängt. Wie dies funktioniert, seht ihr in dem Webcast. An dieser Stelle will ich noch den Code veröffentlichen und um Feedback bitten.

 

 

Extension Methods für IWindsorContainer:

   1: static class CastleWindsorExtensions

   2:     {

   3:         public static IWindsorContainer EnableDependencyTracking(this IWindsorContainer container)

   4:         {

   5:             var containerInfo = new ContainerDependencyTracker();

   6:  

   7:             container

   8:                 .Register(Component.For<ITrackContainerDependencies>()

   9:                 .Instance(containerInfo)

  10:                 .LifestyleSingleton());

  11:  

  12:             container.Kernel.ComponentCreated += containerInfo.AddRequiredAssembly;

  13:  

  14:             return container;

  15:         }

  16:  

  17:         public static IWindsorContainer EnableMemoryDiagnostic(this IWindsorContainer container)

  18:         {

  19:             //have a look at http://docs.castleproject.org/Windsor.Performance-Counters.ashx?HL=lifecycledcomponentsreleasepolicy

  20:             var diagnostic = LifecycledComponentsReleasePolicy

  21:                                 .GetTrackedComponentsDiagnostic(container.Kernel);

  22:             var counter = LifecycledComponentsReleasePolicy

  23:                                 .GetTrackedComponentsPerformanceCounter(new PerformanceMetricsFactory());

  24:             container.Kernel.ReleasePolicy = new LifecycledComponentsReleasePolicy(diagnostic, counter);

  25:  

  26:             return container;

  27:         }

  28:     }

 

Interface für die Tracking Implementierung:

   1: public interface ITrackContainerDependencies

   2: {

   3:     IEnumerable<Assembly> RequiredAssemblies { get; }

   4:  

   5:     int RegisteredComponents { get; set; }

   6:  

   7:     void AddRequiredAssembly(ComponentModel model, object instance);

   8:  

   9:     StringBuilder ResolvedComponentsInARow { get;}

  10: }

 

Implementierung für das Tracking:

   1: class ContainerDependencyTracker : ITrackContainerDependencies

   2: {

   3:     public ContainerDependencyTracker()

   4:     {

   5:         ResolvedComponentsInARow = new StringBuilder();

   6:     }

   7:  

   8:     readonly List<Assembly> _requiredAssemblies = new List<Assembly>();

   9:  

  10:     int Number;

  11:     public StringBuilder ResolvedComponentsInARow

  12:     {

  13:         get;

  14:         private set;

  15:     }

  16:  

  17:     public IEnumerable<Assembly> RequiredAssemblies

  18:     {

  19:         get

  20:         {

  21:             return _requiredAssemblies.Distinct().ToList();

  22:         }

  23:     }

  24:  

  25:     public int RegisteredComponents

  26:     {

  27:         get;

  28:         set;

  29:     }

  30:  

  31:     public void AddRequiredAssembly(ComponentModel model, object instance)

  32:     {

  33:         Number += 1;           

  34:         ResolvedComponentsInARow.AppendLine(string.Format("{0}: {1}", Number, model.Implementation));

  35:  

  36:  

  37:         foreach (var service in model.Services)

  38:             _requiredAssemblies.Add(service.Assembly);

  39:  

  40:         _requiredAssemblies.Add(model.Implementation.Assembly);

  41:     }

  42: }

 

In dem Video gehe ich auch kurz auf die Auffindung von Speicherlecks ein. Die Implementierung ist oben in den Extensions enthalten. Es sei an dieser Stelle auf folgende Seite im Castle Wiki verwiesen.

Einführung in IoC Container

In diesem Webcast biete ich einen kleinen Einstieg zur Verwendung von IoC Containern. Diese Frameworks helfen bei der Umsetzung des Inversion of Control Principle unter Einsatz von Dependency Injection. Damit ist es möglich gemeinsam an an einem Feature zu arbeiten und so den Work in Progress (WiP) zu minimieren.

  • Alle Blogeinträge zu Castle.Windsor samt den im Video gezeigten Code Beispielen gibt es hier.
  • Die Projekte aus dem Webcast habe ich vorübergehend hier zur Verfügung gestellt.

 

Modularisierung durch Dependency Injection

Webcast Strategy Pattern mit IoC

Den Webcast zu diesem Blogeintrag gibt es hier.

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.

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

ReSharper & IoC Container

Wer in seiner Architektur auf IoC Container einsetzt, der bekommt bei ReSharper immer die Meldung “Possible NullReferenceException”, wenn er z.B. im Konstruktor den NULL-check unterlässt. Allerdings ist diese Prüfung meiner Meinung nach unnötig, da der Container ja entweder eine Instanz erzeugen kann oder ggf. eine Exception schmeißt. Deshalb würde ich diese Prüfung in ReSharper abschalten:

Geht dazu in eure ReSharper Optionen (Achtung: Ab Version 6.1 gibt es ja unterschiedliche Layer an Optionen. Achtet darauf, dass ihr im richtigen Layer seid!). Danach findet ihr unter Code Inspection – Inspection Severity im Tab All, wenn ihr beim Filter “null” eingebt unter der Kategorie “Potential Code Quality Issues” die Option “Possible System.NullReferenceException”, welche ihr auf “Do not show” stellen müsst.

image

Using an IoC-Container right

I noticed that some people don’t use their IoC-Container in the manner they should to. They inject the container into their objects like this:

   1: public class Foo : IFoo

   2: {

   3:     private IContainer _container;

   4:  

   5:     public Foo(IContainer container) {

   6:         _container = container;

   7:     }

   8:  

   9:     public Boo GetBooForFoo(int id) {

  10:         var boo = _container.Resolve<IMyService>().GetBooById(id);

  11:         return boo;

  12:     }

  13: }

A friend of mine (Web Developer) uses the same approach (not with an IoC but an array) in PHP: He injects an array that contains every dependency. When i asked him why he is doing this that way, he told me that he doesn’t want his API to change when he needs more dependencies.

So why is this a bad decision:

  • Fail Fast Principle: If the requested service IMyService in line 10 is not found, it will raise an Exception (for example an ComponentNotFoundException). In this scenario that’s no fracture of the leg but what if you would have executed some important changes before? You shouldn’t be able call a method without having the a valid starting point. It’s much better and cleaner to get an exception when you instantiating the object Foo.
  • Keep in mind why you are using an container: You want to implement the Inversion of Control principle. It’s not your call that should be in control, it’s your container. It’s paradox to use an IoC-Container when you develop your code to annul the basic principle behind. Another point is that you have a hard coded dependency to a specific container implementation so you’re not able to switch you IoC-Container.
  • And my last consideration is about the benefit that you’re api won’t change: Don’t hide dependencies! If you’re not the only developer that works with that code, you will get big problems because you change the components behaviour without getting errors. A developer that uses this component is wondering why his program is not working correctly any longer. Or – and that would be really upsetting – the calling component works in some cases and in some cases not for example if line 10 would be in a switch statement.

Too make a long story short: Here’s my approach

   1: public class Foo : IFoo

   2: {

   3:     private IMyService _myService;

   4:     

   5:     public Foo(IMyService myService) {

   6:         //you might want to do a null check

   7:         //what's not necessary if you unit tested your IoC right

   8:         _myService = myService

   9:     }

  10:  

  11:     //you should know what to do now

  12: }

Inversion of Control and Entity Framework

Today i want to show you my approach to resolve the requested entity framework object context at runtime. Starting point:

  • First, i want to have just one implementation of an IUnitOfWork to resolve any kind of repository like Repository<Order>
  • Second, my unit of work should autodiscover the proper context for my requested type of domain model as i have different databases and object contexts. For example, Order is a domain object of my domain model (namespace Domain.ComWork) according to the context ‘comwork’, which is mapping to the database ‘comwork’. There’s another domain object like Customer in my domain model (namespace Domain.MyHeco) according to the context ‘myheco’, which is mapping to the database ‘myheco’.

So how can i achieve this? I will use an IoC container like Castle.Windsor. You can add it to your Visual Studio project by the nuget command “install-package Castle.Windsor”. I want to mention that this scenario needs a little bit of advanced IoC techniques because the dependent component has to be resolved at runtime. So you’re not able to register a dedicated component for an interface. Instead you’ve got to use a facility. Allright, so we tell the container that we will register a facility:

   1: container.AddFacility<TypedFactoryFacility>();

 

In line 1 we tell the container that it has to load the necessary infrastructure for facilities. Let’s take a look at the registration process. Here’s my method that will return me an IEnumerable of IRegistration.

   1: static IEnumerable<IRegistration> Components()

   2: {

   3:     yield return Component

   4:         .For<IContextFactory>()

   5:         .AsFactory(c => c.SelectedWith<ContextFactoryHandlerSelector>());

   6:     yield return Component

   7:         .For<ContextFactoryHandlerSelector>();

   8:     yield return Component

   9:         .For<IUnitOfWork>()

  10:         .ImplementedBy<UnitOfWork>();

  11:     yield return Component

  12:       .For<ComWorkContext>()

  13:       .Named("comworkcontext");

  14:     yield return Component

  15:       .For<MyHecoContext>()

  16:       .Named("myhecocontext");

  17: }

 

Here’s the good news: Castle Windsor has an out of the box feature that creates you the needed factory. In other words: The container creates automatically the factory. You’re code is unaware of the container. Take a look at this article. If you want to use this you’ve have to follow some conventions. In my approach i decided to develop my own implementation to follow my own convetions. Take a look at line 5.  With “.AsFactory” i tell the container that i don’t register a concrete implementation for that service (IContextFactory). If i would follow the conventions of Castle.Windsor calling AsFactory() would be enough.

Here’s the code for my own implementation of a TypedFactoryComponentSelector (this would be auto generated by using just AsFactory without parameters):

   1: public class ContextFactoryHandlerSelector : ITypedFactoryComponentSelector

   2: {

   3:     public TypedFactoryComponent SelectComponent(MethodInfo method, 

   4:         Type type, object[] arguments)

   5:     {

   6:         var requestFor = method.GetGenericArguments().First();

   7:         

   8:         var @namespace = string.Format("{0}context", 

   9:             requestFor.Namespace.ToLower()

  10:             .Replace("domain.",""));

  11:         

  12:         return new TypedFactoryComponent(@namespace, method.ReturnType, null);

  13:     }

  14: }

 

My convention is pretty simple: The name of my object context is the same as the namespace of my model without the string “domain.” and with “context” appended.

Sample: Full name of my domain object: “domain.comwork.order”

  • use just the namespace –> domain.comwork
  • replace domain –> comwork
  • add context –> comworkcontext

I have two kind of domain models (domain.comwork and domain.myheco) so i need two contexts (comworkcontext and myhecocontext). Every context is registered:

   1: yield return Component

   2:   .For<ComWorkContext>()

   3:   .Named("comworkcontext");

   4: yield return Component

   5:   .For<MyHecoContext>()

   6:   .Named("myhecocontext");

 

Now we’ve got it: We have a facility that invokes a call to my self implemented factory if a service (defined in the interface IContextFactory) is requested. The factory returns the name of the needed context at runtime. My Interface looks like this:

   1: public interface IContextFactory

   2: {

   3:     ObjectContext GetContextFor<T>();

   4: }

And here’s the generic part. If i call GetContextFor<domain.comwork.order> i will get my comworkcontext. If i call GetContextFor<domain.myheco.customer> i will get my myhecocontext.

The last step is to inject the factory in my UnitOfWork. Nothing easier than that:

   1: public class UnitOfWork : IUnitOfWork

   2: {

   3:     private readonly IContextFactory _factory;

   4:  

   5:     public UnitOfWork(IContextFactory factory)

   6:     {

   7:         _factory = factory;

   8:     }

   9:  

  10:     //here you would return an IRepository<T>

  11:     //this is just a sample code

  12:     public void GetRepository<T>() where T : class 

  13:     {

  14:         Console.WriteLine(_factory.GetContextFor<T>().GetContextName());

  15:     }

  16: }

 

And how can i resolve my concrete UnitOfWork in my program?

   1: static class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:             using (var container = new WindsorContainer())

   6:             {

   7:                 container.AddFacility<TypedFactoryFacility>();

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

   9:  

10: container.Resolve<IUnitOfWork>()

.GetRepository<Domain.ComWork.Foo>();

11: container.Resolve<IUnitOfWork>()

.GetRepository<Domain.MyHeco.Bar>();

  12:             }

  13:     }

  14:  

  15:     static IEnumerable<IRegistration> Components()

  16:     {

  17:         //Registrations...

  18:     }

  19: }

%d Bloggern gefällt das: