Think First Development

Erst denken, dann programmieren

Durchsuche Beiträge, die in der Kategorie März, 2009 erstellt wurden

Ich habe ja meinen Augen nicht getraut als ich feststellen musste, dass das WebBrowser Control ein ActiveX ist.

Da ich aber von anderen immer zunächst nur das beste denke habe ich mal in die Registry geschaut und siehe an, die GUID ist wirklich ein Internet Explorer Active X.

Jetzt bleibt nur noch offen, ob die “Read Exception” direkt und unmittelbar damit zusammen hängt (bestimmt! ;) ) und was ich da gegen machen kann.

Nachdem ich mich die letzte Woche mit PostSharp beschäftigt habe schreibe ich hier nun mein Ergebnis: AOP mit PostSharp ist einfach genial. So einfach möchte ich es auch nicht machen, also etwas mehr Informationen.

PostSharp ist ein Framework zum Implementieren von Aspekt Orientierter Programmierung (AOP). Das Ziel ist eine nachträgliche und separate Implementierung von Aspekten in eine Applikation. Klassische Beispiele hierfür sind Logging und Security. PostSharp ist so implementiert, dass die Aspekte mittels Attribut an Methoden, Eigenschaften usw. gehangen werden. Der Post-Compiler von PostSharp ändert nach dem Kompilieren durch Visual Studio die Assembly. PostSharp kann hier heruntergeladen werden. Zum Abschluss noch ein interessanter Hinweis. PostSharp kopiert die Assemblies in das Ausgabeverzeichnis die auf dem Client benötigt werden. PostSharp muss auf den Client nicht installiert werden.

Funktionsweise von PostSharp (Bild: PostSharp)

Das folgende Beispiel zeigt eine DoSomething Methode, welche über einen Logging-Aspect nachträglich geloggt wird. Der Code der LoggingAspect Klasse zeigt die Implementierung der Aspects über das PostSharp Framework. Dem Projekt müssen die Referenzen auf die Assemblies “PostSharp.Laos” und “PostSharp.Public” hinzugefügt werden.

    public class Program
    {
        public static int Main(string[] args)
        {
            DoSomething();

            Console.Read();
            return 0;
        }

        [LogAcpect()]
        private static void DoSomething()
        {
            Console.WriteLine("Hello World");
        }
    }

    [Serializable()]
    public class LogAcpect : OnMethodBoundaryAspect
    {
        public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Calling: {0} ---", eventArgs.Method);
        }

        public override void OnExit(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Leaving: {0} ---", eventArgs.Method);
        }

        public override void OnSuccess(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Success: {0} ---", eventArgs.Method);
        }

        public override void OnException(MethodExecutionEventArgs eventArgs)
        {
            Console.WriteLine("--- Exception: {0} @ {1} ---", eventArgs.Exception.Message, eventArgs.Method);
        }
    }

An dieser Stelle noch einen paar Hinweise. Die Klasse LogAspect muss als Serializable deklariert werden, ansonsten gibt es beim Starten einen Fehler. Weiterhin ist die Klasse LogAspect eine Attribut-Klasse und kann wie eine solche mit Eigenschaften und überladenen Konstruktoren implementiert werden. Der folgende Code zeigt eine disassemblierte Version der Funktion. Hier sieht man genau, welche Methoden an welcher Stelle aufgerufen werden und das man mittels FlowBehaviour eine Methode kontrolliert abbrechen kann und das eine Exception in der Aspect-Klasse nach “oben” durchgereicht wird.

private static void DoSomething()
{
    MethodExecutionEventArgs ~laosEventArgs~1;
    try
    {
        ~laosEventArgs~1 = new MethodExecutionEventArgs(~PostSharp~Laos~Implementation.~targetMethod~1, null, null);
        ~PostSharp~Laos~Implementation.LogAcpect~1.OnEntry(~laosEventArgs~1);
        if (~laosEventArgs~1.FlowBehavior != FlowBehavior.Return)
        {
            Console.WriteLine("Hello World");
            ~PostSharp~Laos~Implementation.LogAcpect~1.OnSuccess(~laosEventArgs~1);
        }
    }
    catch (Exception ~exception~0)
    {
        ~laosEventArgs~1.Exception = ~exception~0;
        ~PostSharp~Laos~Implementation.LogAcpect~1.OnException(~laosEventArgs~1);
        switch (~laosEventArgs~1.FlowBehavior)
        {
            case FlowBehavior.Continue:
            case FlowBehavior.Return:
                return;
        }
        throw;
    }
    finally
    {
        ~PostSharp~Laos~Implementation.LogAcpect~1.OnExit(~laosEventArgs~1);
    }
}

Die Implementierung eines Security-Aspect hängt von den Bedürfnissen der Applikation ab. In meinem aktuellen Projekt haben wir in einer Datenbank einem Benutzer eine oder mehrere Funktionen zugewiesen. Der SecurityAspect prüft gegen eine IFunctionProvider-Schnittstelle, ob dem Benutzer die entsprechenden Funktion zugeordnet wurde. Die Funktion wird über den Konstruktor der SecurityAspect-Attibutes angegeben. Besteht der Anwender die Sicherheitsabfrage nicht, wird eine SecurityException geworfen. Diese Exception kann mittels Try-Catch abgefangen werden. Eine solche Implementierung stellt sicher, dass Methoden nur dann ausgeführt werden, wenn die Sicherheitsrichtlinien erfüllt werden. Die GUI stellt selbstverständlich a priori sicher, dass eine solche Methode nicht aufgerufen werden kann, wenn der Anwender keine hinreichenden Berechtigungen besitzt.

        [SecurityAcpect(FunctionEnumeration.DoSomething)]
        private static void DoSomething()
        {
            Console.WriteLine("Hello World");
        }

Die aspektorientierte Programmierung ist nur ein Aspekt des Postsharp Frameworks. Die Stärke des Frameworks liegt in der, wie auch immer gearteten, nachträglichen Modifizierung des Codes.

Das Erweitern des Tools “stsadm.exe” zum Verwalten des MOSS 2007 ist mit .NET nicht sehr aufwändig. Es muss eine Klasse geschrieben werden, welche die “ISPStsadmCommand” Schnittstelle implementiert. Die Assembly in der sich die Klasse befindet muss Signiert werden und anschließend muss eine XML Konfigurationsdatei, die eine Verbindung mit dem stsadm-Command herstellt, geschrieben werden.

Dieses Beispiel ist in der Lage, eine Seite rekursiv zu löschen, da das “deleteweb” Command ist nicht in der Lage ist, ein SPWeb mit Unterseiten zu löschen.

    /// <summary>
    /// Klasse für die Schnittstelle ISPStsadmCommand zum Bereitstellen des stsadm Befehls.
    /// </summary>
    public class RDeleteWeb : ISPStsadmCommand
    {
        #region ISPStsadmCommand Members

        /// <summary>
        /// Anzeigen der Hilfe
        /// </summary>
        public string GetHelpMessage(string command)
        {
            StringBuilder res = new StringBuilder();
            Assembly asm = Assembly.GetAssembly(this.GetType());
            res.AppendLine("Thomas Mentzel - (C) 2007 Softwareküche");
            res.AppendLine("STSADM Extensions Version " + asm.ToString());
            res.AppendLine();
            res.AppendLine("usage:");
            res.AppendLine("   -url <URL>");
            res.AppendLine("");
            return res.ToString();
        }

        /// <summary>
        /// Einstiegspunkt üfr das STSADM
        /// </summary>
        public int Run(string command, System.Collections.Specialized.StringDictionary keyValues, out string output)
        {
            Assembly asm = Assembly.GetAssembly(this.GetType());
            output = "Thomas Mentzel - (C) 2007 Softwareküche";
            output += "STSADM Extensions Version " + asm.ToString();

            //Wenn kein Parameter, dann Infofenster
            if (keyValues.Count <= 1)
            {
                output = GetHelpMessage(command);
                return 0;
            }

            // Nur Command "RemoveInheritedPermissions" durchführen
            if (String.Compare(command, "RDeleteWeb", true) == 0)
            {
                output += DoRDeleteWeb(keyValues);
                return 0;
            }
            else
            {
                throw new InvalidOperationException("Funktion wird nicht unterstützt. " + command);
            }
        }

        #endregion

        #region DoRDeleteWeb

        /// <summary>
        /// Recursive deletes then web and all subwebs.
        /// </summary>
        private string RDeleteAWeb(SPWeb web)
        {
            StringBuilder res = new StringBuilder();
            res.AppendLine("Deleting " + web.Url);

            if (web.Webs.Count > 0)
            {
                foreach (SPWeb subweb in web.Webs)
                {
                    res.Append(RDeleteAWeb(subweb));
                }
            }
            web.Delete();

            return res.ToString();
        }

        /// <summary>
        /// Recursive deletes then web and all subwebs.
        /// </summary>
        private string DoRDeleteWeb(System.Collections.Specialized.StringDictionary keyValues)
        {
            SPSite site = new SPSite(keyValues["url"]);
            SPWeb web = site.OpenWeb();
            StringBuilder res = new StringBuilder();
            res.AppendLine(RDeleteAWeb(web));
            return res.ToString();
        }

        #endregion
    }

Der nächste Schritt ist das Erzeugen einer signierten Assembly und das Erzeugen der XML Datei mit folgender Beispieldaten:

<?xml version="1.0" encoding="utf-8" ?>
<commands>  

    <command class="StsadmCommand.RDeleteWeb, StsadmCommand, Version=1.0.0.0, Culture=neutral, PublicKeyToken=affe000020021980" name="RDeleteWeb" />  

</commands>

Der .NET Reflector hilft dabei, die vollständigen Assembly-Informationen auszulesen.

Schließlich muss die Assembly nach “%CommonProgramFiles%Microsoft SharedWeb Server Extensions12bin” und das XML nach “%CommonProgramFiles%Microsoft SharedWeb Server Extensions12conf” kopiert werden.

Nun sollte beim Aufruf von “stsadm.exe” ohne Parameter eine Liste die Operation des “stsadm.exe” inklusive “rdeleteweb” erscheinen.

Ich habe mich endlich mal mit den Extension Methods von C# auseinander gesetzt. Das ist ja um vieles einfacher als ich dachte ;) Hier mal ein kleines Beispiel, das den String Datentyp um eine Methode “ToInt32()” erweitert.

    /// <summary>
    /// Erweiterungsfunktionen für Strings
    /// </summary>
    public static class StringExtensions
    {
         /// <summary>
         /// Wandelt einen String in eine Zahl um.
         /// ExtensionMethod für den Datentyp "String"
         /// </summary>
         /// <param name="s">Erweiterter Datentyp</param>
         /// <returns></returns>
         public static int ToInt32(this string s)
         {
             int res;

             // Umwandeln des String, sonst default zurück geben
             if (int.TryParse(s, out res))
                 return res;
             else
                 return default(int);
         }
    }

Die anschließende Verwendung ist denkbar einfach. Im IntelliSense des Visual Studio erscheint die Methode als ganz normale Methode der String Klasse. Diese kann wie jede andere Funktion z.B. “.ToString()” ausgewählt und benutzt werden.

    class Program
    {

        static void Main(string[] args)
        {
            string aNumberAsString = Console.ReadLine();

            Console.WriteLine("IsOdd: " + IsOdd(aNumberAsString.ToInt32()));
            Console.WriteLine("IsEven: " + IsEven(aNumberAsString.ToInt32()));

            Console.ReadKey();
        }

        public static bool IsEven(int number)
        {
            return (number % 2 == 0);
        }

        private static bool IsOdd(int number)
        {
            return (number % 2 == 1);
        }
    }

Powered by WordPress Web Design by SRS Solutions © 2010 Think First Development Design by SRS Solutions