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

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.

Unnötige Warnings in Datenbank Projekten loswerden

Warnings sind eine gute Sache. Sie weisen uns auf Dinge hin, die wir besser machen sollten. Jedoch gibt es auch Warnings die keine Hilfe sind und uns den Blick auf die wichtigen Hinweise versperren. Jeder, der ein größeres Datenbank Projekt mit Visual Studio 2010 verwendet oder verwendet hat weiß sicherlich was ich damit meine.

Es geht hierbei um Warnings, die aufkommen weil wir beispielsweise Informationen aus anderen Datenbanken (meist Master) abfragen. Aus diesem Grund ist es nur verständlich und richtig, das diese Warnings generiert werden, denn das Datenbank Projekt kennt zunächst nur die eigenen Datenstrukturen und weiß mit denen aus anderen Datenbanken nichts anzugfangen.

Warning XX SQL04151: .. contains an unresolved reference to an object. 
Either the object does not exist or the reference is ambiguous because it 
could refer to any of the following objects: ..

Aber was können wir tun, um die Warnings loszuwerden?

Hier gibt es mehrere Wege zum Ziel:

1. Für das Datenbank Projekt global die Warning unterdrücken

Dies geht über die Projekt Eigenschaften –> Reiter Build –> Suppress Warnings. Hier sind die Warnings in ihrer numerischen Ausprägung separiert durch Semikolon anzugeben. In unserem Beispiel betrifft es die Warning 4151.

Dies ist der einfachste und schnellste Weg, aber nicht der zu empfehlende. Diese Warning kann uns auf Fehler in unseren Datenbank Objekten hinweisen.

2. Für das jeweilige Datenbank Objekt die Warning unterdrücken

Um dies zu erreichen muss die Datei des betroffenen Objektes im Solution Explorer angewählt werden. Im Eigenschaften Fenster findet sich die Eigenschaft Suppress Warnings. Hier ist es wieder möglich über Semikolon getrennt mehrere Warnings anzugeben. Im Falle der oben dargestellten Warning wäre 4151 einzutragen.

Dieser Weg ist schon recht gut. Damit unterdrücken wir zwar ggf. Warnings die uns nützlich sein könnten, aber nur noch in einzelnen Dateien.

3. Das Problem an der Wurzel packen und lösen

Hierfür müssen wir die unbekannten Objekte dem Projekt bekannt machen. Dafür gehen wir wieder auf die Eigenschaften des Datenbank Projektes und dann auf den Reiter References. Dort klicken wir auf Add Database Reference. Dort öffnet sich dann ein neuer Dialog, in dem wir ein .dbSchema File oder ein anderes Datenbank Projekt auswählen können:

image

Für unser Beispiel habe ich die Referenz auf das Master Schema hinzugefügt. Wie im Screenshot zu erkennen ist befindet sich dies im Visual Studio Verzeichnis unter VSTSDBSqlServer[VERSION]DBSchemas.

 

Fazit:

In diesem Artikel habe ich mehrere Wege aufgezeigt Datenbank Warnings loszuwerden. Es gibt Situationen in denen die Lösung 3 nicht funktioniert und es nur möglich ist Warnings loszuwerden, indem man diese unterdrückt. Meine Empfehlung würde jedoch immer lauten zuerst versuchen die Ursache zu bekämpfen und erst wenn das nicht funktioniert die Symptome, sprich Lösung 3 dann Lösung 2. Generell abraten würde ich von Lösung 1.

 

Quellenangaben:

http://msdn.microsoft.com/en-us/library/dd193259(v=vs.100).aspx

Querschnittsfunktionen mit der Enterprise Library durch Interception automatisieren

In jedem Projekt gibt es sogenannte Common Tasks, also Querschnittsfunktionalitäten, die häufig und oft auch von mehreren Schichten der Anwendung verwendet werden. Beispielhaft sei an dieser Stelle Logging genannt. Logging ist eine Funktionalität, die aber nichts mit dem eigentlichen Code beziehungsweise dessen Aufgabe zu tun hat und die Methoden erstens aufbläht und zweitens oftmals durch Copy und Paste auch fehlerhaft implementiert wird. Aber es gibt Abhilfe: Aspekt Orientierte Programmierung (AOP). Im folgenden werde ich auf die Möglichkeiten von Unity im Bezug auf AOP eingehen und unter anderem Logging und andere Ansätze zeigen.

Interception ist ein Bestandteil von Unity und damit auch der Enterprise Library. Die Enterprise Library ist kostenlos zum Download erhältlich. Der aspektorientierte Ansatz wird bei Interception im Gegensatz zu anderen Frameworks (bspw. Postsharp) über Proxy Objekte gelöst und nicht über den Compile Vorgang. Dies bringt einige Einschränkungen mit sich, doch dazu später mehr. Die folgende Grafik zeigt den Interception Mechnismus wie ich finde sehr anschaulich:

image

Quelle: http://msdn.microsoft.com/en-us/library/ff660861(v=pandp.20).aspx

Der Client (1) ist in diesem Fall ihre Applikation. Über die Unity Api (2) lässt man sich seine Klasseninstanz erzeugen. Hier wird auch sichtbar, dass man keine direkte Instanz der angeforderten Klasse erhält, sondern ein Proxy Objekt (3). Dieses Proxy Objekt gibt alle Aufrufe an die implementierten Behaviors (4) weiter (es können mehrere Behaviors auf eine Instanz angewendet werden deswegen auch Behavior Pipeline) die Behaviors wiederum erfüllen ihre Aufgabe und führen am Zielobjekt (5) dann die eigentliche Funktionalität (Methode/Property) aus.

Vorweg sei noch gesagt, dass es nicht notwendig ist den Dependency Injection Mechnismus von Unity zu verwenden, um Interception einzusetzen. Die Interception Funktionalität enthält eine eigene API. Nichts desto trotz ist es selbstverständlich möglich Interception in Verbindung mit dem Dependency Application Block zu nutzen. Ich werde in dieser Artikelserie beide Ansätze aufzeigen.

Generell gibt es bei Interception drei Möglichkeiten der Implementierung:

1. Transparent Proxy Interception

Hierbei müssen die Klassen, die um Funktionalität erweitert werden sollen, von der Basisklasse MarshalByRef abgeleitet werden. Ich persönlich bin kein Fan dieses Ansatzes, da dieser die Code Architektur und die verwendbaren Klassen von vornherein einschränkt. Ein weiterer Nachteil ist die schlechtere Performance im Vergleich mit den anderen Interception Ansätzen.

2. Interface Interception

Zur Verwendung der Interface Interception werden wie der Name schon sagt Interfaces verwendet. Außerdem muss jede Methode, auf die Interception angewendet werden soll, muss zwingend im Interface veröffentlicht sein. Dies ist mein bevorzugtes Verfahren.

3. Virtual Method Interception

Hierbei wird auf alle virtuellen Methoden der Klasse Interception angewendet.

Ich werde im Folgenden nur den Ansatz der Interface Interception verfolgen, da dieser in meinen Augen der praxisrelevanteste ist.

Die Klasse Dummy

Für die Code-Beispiele habe ich kurzerhand eine einfache Klasse mit einem Interface entworfen, diese wird im Verlauf des Artikels noch häufiger verwendet:

public class Dummy : IDummy
    {
        public int AddiereEtwas(int a, int b)
        {
            return a + b;
        }

        private string _Name;
        
        public string Name
        {
            get
            {
                if(string.IsNullOrEmpty(_Name))
                    return "Ich heisse Dummy";
                return _Name;
            }
            set { _Name = value; }
        }

        public void TueEtwasAnderes(string a, bool b)
        {
            VerheirateStringMitBool(a, b);
        }

        private string VerheirateStringMitBool(string a, bool b)
        {
            return a + b.ToString();
        }

        public static bool WurdeEineZahlKleinerZehnUebergeben(int zahl)
        {
            return zahl < 10;
        }
    }

Diese Klasse tut natürlich nichts sinnvolles und soll lediglich die Möglichkeiten und auch Grenzen von Interception aufzeigen.

Für diese Klasse benötigen wir jetzt noch das passende Interface:

    interface IDummy
    {
        int AddiereEtwas(int a, int b);

        void TueEtwasAnderes(string a, bool b);

        string Name { get; set; }
        
    }

Spätestens an dieser Stelle wird klar, dass durch den Interface Mechanismus an sich bereits die natürlichen Einschränkungen bezüglich Interfaces existieren (Public Methoden und Properties, nichts statisches …).

Nun haben wir bereits eine Klasse und ein Interface. Der Artikel hat ja in der Überschrift versprochen “common Tasks” zu automatisieren. Hierfür benötigen wir noch ein oder mehrere Behaviors. Das Behavior implementiert diese Common Aufgabe.

Behaviors

Mit Behaviors lassen sich Querschnittsfunktionen umsetzen die dann mit AOP-Mechanismen in bestehende Funktionen eingewebt werden. Der Aufruf einer Methode wird vom Behavior gekapselt. Damit ist es möglich Aktionen vor und nach dem Methodenaufruf durchzuführen. Hier ist (m)eine sehr simple Implementierung für Logging:

public class MyLoggingBehaviour : IInterceptionBehavior
    {
        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            Console.WriteLine("MyLoggingBehaviour Start");
            Console.WriteLine("Method: " + input.MethodBase);

            for (int i = 0; i < input.Inputs.Count;i++ )
            {
               ParameterInfo param =  input.Inputs.GetParameterInfo(i);
               Console.WriteLine(string.Format("Type: {0} Name: {1} Wert {2} before calling", param.ParameterType, param.Name, input.Inputs[i]));
            }

            IMethodReturn result = getNext()(input,getNext);

            Console.WriteLine("Result was: " + result.ReturnValue);

            Console.WriteLine("MyLoggingBehaviour End");
            
            return result;
        }

        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }

        public bool WillExecute
        {
            get
            {
                return true;
                
            }
        }
    }

Der eigentliche Methodenaufruf passiert bei

IMethodReturn result = getNext()(input,getNext); 

rund um diesen Aufruf sollte man die gewünschte Funktionalität implementieren. Wie im Code oben zu sehen ist, ist getNext ein Delegat, dieser gibt wiederum einen Delegat zurück. Damit wird die komplette Pipeline aller Behaviors (ja, es kann mehrere geben) durchlaufen und schlussendlich die eigentliche Methode aufgerufen. Besonders schön an Behaviors ist, das wie oben zu sehen ist Zugriff auf alle Parameter und deren Werte, sowie auf den Rückgabewert der eigentlichen Methode möglich ist.

So wir haben eine Klasse, ein Interface und das Behavior. Jetzt müssen wir nur noch alles zusammenstecken.

Implementieren von Interface Interception mit der Standalone API

Für die Standalone API ohne Unity benötigt man die Microsoft.Practices.Unity.Interception.dll. Die DLL kommt bei der Enterprise Library Installation mit und kann über Add Reference dem Projekt hinzugefügt werden.

Die statische Intercept Klasse bringt alles mit, was wir benötigen.

Hier die Definition der Methode ThroughProxy, mit der wir Instanzen von Klassen erzeugen können auf die wir Interception anwenden wollen:

 public static T ThroughProxy<T>(T target, IInstanceInterceptor interceptor, IEnumerable<IInterceptionBehavior> interceptionBehaviors) where T : class


Angewendet auf unsere Dummy Klasse sieht die Implementierung wie folgt aus:

var dummyInstance = Intercept.ThroughProxy<IDummy>(new Dummy(),new InterfaceInterceptor(), new IInterceptionBehavior[]
                                                                                          {
                                                                                              new MyLoggingBehaviour(),
                                                                                          });

Mit new Dummy() übergeben wir eine neue Instanz unserer IDummy Implementierung. Der Befehl new InterfaceInterceptor() übergibt den jeweiligen Interceptor und als letzten Parameter können wir beliebig viele Behaviors übergeben.

Zur weiteren Veranschaulichung habe ich ein kleines Programm geschrieben, das eine Dummy via Interception Instanz erzeugt und das Logging Behavior zuweist. Anschließend wird wir Methode AddiereEtwas aufgerufen.

 

    class Program
    {
        static void Main(string[] args)
        {
            var dummyInstance = Intercept.ThroughProxy<IDummy>(new Dummy(),new InterfaceInterceptor(), new IInterceptionBehavior[]
                                                                                          {
                                                                                              new MyLoggingBehaviour(),
                                                                                          });

            dummyInstance.AddiereEtwas(1, 2);
            Console.ReadKey();
        }
    }

Wenn man das Programm ausführt, so wird auf der Console folgendes ausgegeben:

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

Voilá!

Fazit:

In diesem Artikel habe ich die grundlegenden Interception Mechanismen und eine einfache Implementierung ohne Dependency Injection aufgezeigt.

In den folgenden Artikeln dieser Serie werde ich verschiedene Interfaces verwenden, um unterschiedliche Methoden einer Klasse mit unterschiedlichen Behaviors zu behandeln. Dabei werde ich auch die Implementierung mit Unity Dependency Injection aufzeigen.

Quellenangaben und nützliche Informationen:

Hands-On Labs for Microsoft Enterprise Library 5.0

Unity Interception Techniques

MSDN-Magazin: Innovation – Abfangfunktionen in Unity 2.0

Silverlight Performanceprobleme mit dem BusyIndicator Control

Neulich ist mir beim Schreiben einer Silverlight Anwendung aufgefallen, dass die CPU Auslastung meiner Anwendung permanent über 20% lag und dies, obwohl keinerlei sichtbare Animationen liefen oder ich eine Aktion ausgelöst hätte.

 

Um die Ursache zu erforschen fügte ich zunächst die Zeile

   1: <param name="enableRedrawRegions" 

   2: value="true"/>

in der Default.aspx (im silverlightControlHost div) ein.

Dies bewirkt, dass GUI-Aktualisierungen grafisch dargestellt werden (generell sehr empfehlenswert zu Analysezwecken und insbesondere bei hoher CPU-Last).

Das Ergebnis war überraschend:

=> Das gesamte Anwendungsfenster flackerte permanent bunt. Was ist hier schief gelaufen?

Nach einigem Suchen fand ich die Ursache, ich hatte das Silverlight Business Application Template verwendet, dass von Haus aus ein BusyIndicator Control mitbringt. Durch Auskommentieren des Controls konnte ich verifizieren, dass die andauernde CPU Auslastung durch dieses Control verursacht wurde.

Die Lösung:

Die ProgressBar im Busyindicator Control hat das Property IsIndeterminate. Dieses ist per Default auf “True” gesetzt.

   1: <Setter Property="IsIndeterminate" Value="True"/>

Dadurch läuft die Animation des Ladebalkens permanent in einer Endlosschleife.

Dieses Verhalten kann man abstellen, in dem man im Hidden-VisualState (Control ist ausgeblendet) den Wert IsIndeterminate auf false und im Visible State wieder auf true setzt:

   1: <!-- set indeterminate in Hidden State -->

   2: <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" 

   3:                                Duration="00:00:00.001"

   4:                                Storyboard.TargetName="progressbar"

5: Storyboard.TargetProperty="(UIElement.IsIndeterminate)">

   6:     <DiscreteObjectKeyFrame KeyTime="00:00:00">

   7:         <DiscreteObjectKeyFrame.Value>

   8:             <sys:Boolean>False</sys:Boolean>

   9:         </DiscreteObjectKeyFrame.Value>

  10:     </DiscreteObjectKeyFrame>

  11: </ObjectAnimationUsingKeyFrames>

Fazit:

Was mir an Silverlight besonders gut gefällt, ist, dass man (Dank XAML) diverse Möglichkeiten hat das Verhalten und Aussehen von Controls zu steuern. Die Controls sind keine Blackboxes wie dieses Beispiel zeigt. Das Verhalten des Progressbars konnte ich einfach (über den Visual State Manager) im XAML anpassen. Allerdings erfordert dieses Vorgehen auch immer fundierte Kenntnisse über die Möglichkeiten und Mächtigkeit von XAML.