Querschnittsfunktionen mit der Enterprise Library durch Interception automatisieren (Teil 2)

9. Januar 2013

Im zweiten Teil dieser Serie werde ich aufzeigen, wie Interception mit den Dependency Injection Mechanismen von Unity umgesetzt werden kann. Der erste Artikel mit den Codebeispielen und einer Einführung in das Thema Interception befindet sich hier. Dieser Artikel wird sich nicht ausführlich mit dem Dependency Injection Block von Unity beschäftigen und sollte auch nicht als Tutorial für dieses Thema verstanden werden. Vielmehr setze ich in diesen Artikel voraus, das dieses Wissen bereits vorhanden ist und zeige lediglich das Zusammenspiel mit zwischen dem Dependency Injection Application Block und dem Interception Application Block auf.

Die Dependency Injection Funktionalität von Unity findet sich im Assembly Microsoft.Practices.Unity.dll. Dieses muss selbstredend als Referenz in das Projekt eingefügt werden. In diesem Beispiel werde ich die Klasse Dummy und das passende Interface IDummy verwenden. Diese habe ich im ersten Teil dieser Serie vorgestellt. Im weiteren Verlauf dieses Artikel werde ich Dependency Injection mit DI abkürzen.

Im DI Block ist das zentrale Element der UnityContainer. Über diesen werden konkrete Klasseninstanzen erzeugt:

IUnityContainer container  = new UnityContainer();

Um Typen in dem Container zu registrieren muss die Methode RegisterType<Interface, Klasse> des Containers aufgerufen werden, um eine Instanz der Klasse zu erhalten container.Resolve<Interface>. An dieser Stelle stellt sich bereits die Frage wie auf diesem Wege Interception ins Spiel gebracht werden kann. Irgendwie müssen wir dem Container doch sagen können, dass er unser Behavior verwenden soll.

Die Lösung sieht wie folgt aus:

1. Der Container muss irgendwie erfahren, dass es Interception gibt.

Hierfür muss die generische Methode AddNewExtension mit dem Type Interception aufgerufen werden:

container.AddNewExtension<Interception>();

2. Jetzt kennt der Container immerhin schon Interception, aber unser Behavior muss noch ins Spiel gebracht werden.

Hierfür kann die RegisterType Methode (dank zahlreicher Überladungen) verwendet werden:

/// <summary>
/// Register a type mapping with the container.
/// </summary>
/// <remarks>
/// <para>
/// This method is used to tell the container that when asked for type <typeparamref name="TFrom"/>,
/// actually return an instance of type <typeparamref name="TTo"/>. This is very useful for
/// getting instances of interfaces.
/// </para>
/// <para>
/// This overload registers a default mapping and transient lifetime.
/// </para>
/// </remarks>
/// <typeparam name="TFrom"><see cref="Type"/> that will be requested.</typeparam>
/// <typeparam name="TTo"><see cref="Type"/> that will actually be returned.</typeparam>
/// <param name="container">Container to configure.</param>
/// <param name="injectionMembers">Injection configuration objects.</param>
/// <returns>The <see cref="UnityContainer"/> object that this method was called on (this in C#, Me in Visual Basic).</returns>
public static IUnityContainer RegisterType<TFrom, TTo>(this IUnityContainer container, params InjectionMember[] injectionMembers) where TTo : TFrom

Im konkreten Anwendungsfall sieht die Verwendung dann wie folgt aus:

container.RegisterType<IDummy, Dummy>(new Interceptor<InterfaceInterceptor>(),
                                       new InterceptionBehavior(new MyLoggingBehaviour()));

Der Rest ist nun trivial. Über die Methode Container.Resolve holen wir uns nun die Klasseninstanz und verwenden diese. Interception wird nun automatisch verwendet:

var dummyinstance = container.Resolve<IDummy>();
dummyinstance.AddiereEtwas(1, 2);

Hier nun das Konsolenprogramm (inklusive auskommentiertem Code aus Teil 1) zum Vergleich:

class Program
{
    private static IUnityContainer container;

    static void Main(string[] args)
    {
        container = new UnityContainer();
        container.AddNewExtension<Interception>();

        container.RegisterType<IDummy, Dummy>(new Interceptor<InterfaceInterceptor>(),
                                       new InterceptionBehavior(new MyLoggingBehaviour()));

        var dummyInstance = container.Resolve<IDummy>();

        /*
            Teil 1: INterception ohne Unity DI
            
            container.RegisterType<IClassAInterception, ClassA>(new Interceptor<InterfaceInterceptor>(),
                                                    new InterceptionBehavior(new MyExceptionBehaiour())); 
            

            var dummyInstance = Intercept.ThroughProxy<IDummy>(new Dummy(),new InterfaceInterceptor(),                                                          new IInterceptionBehavior[]
                                                                                          {
                                                                                              new MyLoggingBehaviour(),
                                                                                          });
        */
        dummyInstance.AddiereEtwas(1, 2);
        Console.ReadKey();
    }
}

Die Ausgabe des Programms liefert nun das identische Ergebnis wie im Teil 1:

MyLoggingBehaviour Start
Method: Int32 AddiereEtwas(Int32, Int32)
Type: System.Int32 Name: a Wert 1 before calling
Type: System.Int32 Name: b Wert 2 before calling
Result was: 3
MyLoggingBehaviour End

Fazit:

Der Unity DI Application Block arbeitet hervorragend mit dem Dependency Injection Block zusammen. Dies macht die Verwendung beider Technologien zusammen intuitiv und einfach.