.NET Blog   ·   .NET Casts   ·   .NET GUI Foren   ·   .NET BlogBook   ·   WPF Blogger   ·   visual studio one   ·   ASP.NET professional

  • ACHTUNG - NEUES BLOG

    Ab sofort steht unter http://devtyr.norberteder.com mein neues Blog zur Verfügung. Dieses Blog wird nicht weiter betreut, bleibt aber erhalten. Neue Eintr%auml;ge erfolgen nur mehr im neuen Blog. Kommentare werden ebenfalls nicht mehr behandelt. Wer weiterhin meinen Einträgen und Aktivitäten folgen möchte, möge bitte RSS-Feeds, Verlinkungen etc. an die neue Location anpassen.
Download .NET Essentials Installer
Trickkiste

WPF und MVC

08.06.08 - Entwicklung, Diskussionen, Patterns, .NET, WPF
Beitrag von Norbert Eder
 Anwendungen sollten nicht nur schön anzusehen sein, sondern auch das robust und korrekt tun, wofür sie geschaffen wurden. Damit dem so ist, muss sich der Entwickler/Architekt natürlich zu Beginn eines Projektes so einige Gedanken machen. Denn nur durch ein gutes Grundgerüst werden Anforderungen á la Erweiterbarkeit, Testbarkeit, einfache Wartung usw. auch erfüllt.

Das Model View Controller Pattern ist da so ein Ansatz. Mittlerweile Jahrzehnte am Buckel hatte sich dieses Pattern immer wieder bewährt (in seiner ursprünglichen Form oder in einer abgewandelten).

Wie nun die Windows Presentation Foundation mit dem Model View Controller Pattern zusammen arbeitet, zeigt der Artikel MVC Pattern mit WPF verwenden, welchen ich gestern auf .NET GUI veröffentlicht habe.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


.NET BlogBook Ausgabe 6

14.04.08 - Entwicklung, Diskussionen, Patterns, Software Testing, Projektmgmt., Qualitätsmgmt., .NET, Grundlagen, Base Framework, WPF, ASP.NET, Silverlight, Mobile Devices, Datenverwaltung, Visual Studio, Allerlei, Microsoft Office, SQL Server
Beitrag von Norbert Eder
 Ab sofort steht die 6. Ausgabe des .NET BlogBooks zur Verfügung. Insgesamt stehen nun fast 330 Seiten an puren Informationen und Praxiswissen zur Verfügung.

Noch dazu wurden einige Anregungen aufgegriffen. Es gibt ein neues Cover (ein herzliches Dankeschön an 69° media solutions). Ebenfalls wurden unnötige dunkle Stellen entfernt, die beim Ausdrucken maximal Toner verbrauchen, sonst jedoch keinerlei Wirkung erzielen.

Hauptsächlich wurde das BlogBook um Wissen rund um die Windows Presentation Foundation erweitert, aber auch andere Punkte kamen hinzu. Ein Blick lohnt sich allemal.

Weitere Informationen sind auf der Homepage unter http://www.dotnet-blogbook.com zu finden.

Für Anregungen, Wünsche und (konstruktive) Kritik haben wir natürlich weiterhin ein offenes Ohr.
  6 Kommentare - 1357 mal angesehen   |  1 Trackbacks   |  Permalink  |  Trackback-URL


Undo und Redo implementieren

12.03.08 - Entwicklung, Diskussionen, Patterns, .NET, Grundlagen, Base Framework, WPF, ASP.NET
Beitrag von Norbert Eder
 Es kommt ja doch häufig vor, dass eine Anwendung ein Undo bzw. ein Redo unterstützen muss. Wird diese Funktionalität gefordert wird immer wieder nach einer Umsetzungsmöglichkeit gefragt. Hier eine kleine Lösung, die für diese Zwecke verwendet werden kann.

Hinweis: In diesem Beispiel werden keine Fehlerabfragen gemacht. Es empfiehlt sich, einen Ausführungsstatus zu integrieren, der das Verhalten entsprechend beeinflusst.

Design


Um diese Funktionalität umzusetzen, wird das Command-Pattern verwendet. Dieses eignet sich sehr gut dazu, Aktionen auszuführen und diese auch wieder zurück zu nehmen. Erweitert wird das Command-Pattern um einen CommandManager, der für die Ausführung und Verwaltung der einzelnen Commands zuständig ist.



Implementierung


Zuerst wird eine Basisklasse für den Command implementiert, welche die Methoden Execute(), Undo und Redo zur Verfügung stellt.

public abstract class Command
{
    public abstract void Execute();

    public abstract void Undo();

    public abstract void Redo();
}


Als weiteren Schritt wird der CommandManager implementiert. Dieser ist in diesem Fall ein Singleton (damit er nur einmal pro Instanz vorhanden sein kann) und bietet nach aussen hin die selbe Funktionalität wie ein Command an. Intern werden die einzelnen Commands jedoch in einem Stack behalten, um den Verlauf der einzelnen Commands nachvollziehen zu können. Darüber kann nun ein Undo bzw. ein Redo abgebildet werden.

public class CommandManager
{
    #region Static Attributes

    private static object _lockObject = new object();
    private static CommandManager _instance = null;

    #endregion Static Attributes

    #region Attributes

    private Stack<Command> _commandStack = new Stack<Command>();
    private Stack<Command> _undoneStack = new Stack<Command>();

    #endregion Attributes

    #region ctor

    private CommandManager() { }

    #endregion ctor

    #region Static Methods

    public static CommandManager GetInstance
    {
        get
        {
            if (_instance == null)
            {
                lock (_lockObject)
                {
                    _instance = new CommandManager();
                }
            }
            return _instance;
        }
    }

    #endregion Static Methods

    #region Public Methods

    public void Execute(Command command)
    {
        command.Execute();
        _commandStack.Push(command);
    }

    public void Redo()
    {
        Command redoCommand = _undoneStack.Pop();
        redoCommand.Redo();
        _commandStack.Push(redoCommand);
    }

    public void Undo()
    {
        Command undoCommand = _commandStack.Pop();
        undoCommand.Undo();
        _undoneStack.Push(undoCommand);
    }

    #endregion Public Methods
}


Damit ist die eigentliche Implementierung bereits abgeschlossen und wir können zu einem ersten Test schreiten. Hierzu benötigen wir ein paar kleinere Klassen, die nicht wirklich etwas aufregendes machen.

Hier eine Klasse Person, die zwei Eigenschaften zur Verfügung stellt, anhand derer getestet wird. Zudem implementiert sie das Interface ICloneable (wird im nachfolgenden Command benutzt) und überschreibt die ToString-Methode.

public class Person : ICloneable
{
    #region Properties

    public String Firstname { get; set; }
    public String Lastname { get; set; }

    #endregion Properties

    #region ICloneable Members

    public object Clone()
    {
        Person p = new Person();
        p.Firstname = this.Firstname;
        p.Lastname = this.Lastname;
        return p;
    }

    #endregion ICloneable Members

    #region Overrides

    public override string ToString()
    {
        return String.Format("{0}, {1}", Lastname, Firstname);
    }

    #endregion Overrides
}


Als nächsten Schritt müssen wir noch einen konkreten Command implementieren, der eine Aktion durchführt. Dieser wird sich lediglich ein wenig mit den Eigenschaften der Person spielen:

public class PersonCommand : Command
{
    private Person _person;
    private Person _personStateBefore;

    public PersonCommand(Person person)
    {
        _person = person;
    }

    public override void Execute()
    {
        _personStateBefore = _person.Clone() as Person;

        _person.Firstname = "Norbert";
        _person.Lastname = "Eder";
    }

    public override void Undo()
    {
        _person.Firstname = _personStateBefore.Firstname;
        _person.Lastname = _personStateBefore.Lastname;
    }

    public override void Redo()
    {
        Execute();
    }
}


Wichtig ist an dieser Stelle nur, dass sich der Command den ursprünglichen Status des übergebenen Objektes merkt und somit eine Undo bzw. Redo-Funktionalität anbieten kann.

Schlussendlich ein Stück Code, welches bei mir in einer Konsolen-Anwendung direkt in der static void Main ausgeführt wird:

CommandManager manager = CommandManager.GetInstance;

Person p = new Person();
p.Firstname = "<not set>";
p.Lastname = "<not set>";
Console.WriteLine(
    String.Format("Before first execution: " + p.ToString()));

manager.Execute(new PersonCommand(p));
Console.WriteLine(
    String.Format("After first execution : " + p.ToString()));

manager.Undo();
Console.WriteLine(
    String.Format("After undo            : " + p.ToString()));

manager.Redo();
Console.WriteLine(
    String.Format("After redo            : " + p.ToString()));

Console.Read();


Und nun möchte ich das Ergebnis nicht vorenthalten:



Fazit


Wie zu sehen ist, ist ein Undo bzw. Redo-Mechanismus sehr einfach zu implementieren. Abhängig der Umgebung in welcher diese Methode zu tragen kommt, müssen eventuell weitere Mechanismen eingefügt werden.
  9 Kommentare - 2585 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Verhalten einer Anwendung per Konfiguration bzw. Laufzeit verändern

27.02.08 - Entwicklung, Diskussionen, Patterns, .NET, Grundlagen
Beitrag von Norbert Eder
 

Abgrenzung


In diesem Artikel wird folgendes gezeigt:
  • Austauschen von Verhaltensweisen via Konfiguration als auch zur Laufzeit
  • Austauschen von einzelnen Methoden via Konfiguration als auch zur Laufzeit

Einführung


In vielen Fällen ist es notwendig, Verhaltensweisen über die Konfiguration oder per Laufzeit zu steuern. Als Beispiel: Nehmen wir eine Anwendung, die unterschiedliche Listen zur Verfügung stellt. Nun müssen die Daten für diese Listen geladen werden, damit sie angezeigt werden können. Unter der Annahme, dass alle Daten aus einer Datenbank kommen ist es kaum notwendig, dieses Verhalten zu verändern. Nun kann es aber sein, dass Inhalte für bestimmte Listen nicht aus der Datenbank kommen, sondern aus anderen Quellen. Das Standardverhalten würde nun nicht mehr funktionieren und muss daher ausgetauscht werden.
Ein weiterer häufig auftretender Punkt ist, dass zwar grundsätzlich das Standardverhalten verwendet werden soll, bis auf einen kleinen Teil, beispielsweise eine bestimmte Methode.

Strategy Pattern hilft bei Verhaltensänderungen


Da Verhaltensweisen getauscht werden können, liegt es nahe, sich im Bereich der Behavioral Patterns umzusehen. Darunter ist das Strategy-Pattern zu finden. Dieses ermöglicht, das gesamte Verhalten auszutauschen. Hier eine UML-Übersicht dieses Patterns:



Im Diagramm ist der Aufbau einfach zu erkennen. Grundsätzlich wird ein Interface IStrategy zur Verfügung gestellt. Dieses schreibt die Methode DoWork vor, welche dann die tatsächliche Aufgabe ausführt. Dieses Interface wird von zwei konkreten Klassen implementiert: ConcreteStrategy1 und ConcreteStrategy2. Beide Klassen besitzen also die Methode DoWork. Allerdings unterscheiden sich diese beiden Implementierungen voneinander, sprich das Verhalten ist ein unterschiedliches (sonst würde auch die Erstellung von zwei konkreten Klassen wenig sinnvoll sein). Schließlich gibt es noch einen StrategyContext. Dieser bekommt über den Konstruktor ein IStrategy übergeben. Dieses bestimmt nun das Verhalten. Die Aufrufe erfolgen über den StrategyContext, wodurch die Funktionalität aus dem übergebenen Strategy-Objekt aufgerufen wird. Eine Beispiel-Implementierung sieht so aus:
public interface IStrategy
{
    void DoWork();
}

public class ConcreteStrategy1 : IStrategy
{
    #region IStrategy Members

    public void DoWork()
    {
        Console.WriteLine("ConcreteStrategy1");
    }

    #endregion
}

public class ConcreteStrategy2 : IStrategy
{
    #region IStrategy Members

    public void DoWork()
    {
        Console.WriteLine("ConcreteStrategy2");
    }

    #endregion
}

public class StrategyContext
{
    private IStrategy _strategy;

    public StrategyContext(IStrategy strategy)
    {
        _strategy = strategy;
    }

    public void DoWork()
    {
        _strategy.DoWork();
    }
}

Der Aufruf des Konstruktes geschieht folgendermaßen:
class Program
{
    static void Main(string[] args)
    {
        StrategyContext context = new StrategyContext(new ConcreteStrategy1());
        context.DoWork();
    }
}

Austausch von einzelnen Methoden


Bisher haben wir gesehen, wie gesamte Verhaltensweisen ausgetauscht werden können. Wie sieht es jedoch aus, wenn nur einzelne Methoden getauscht werden und das restliche Verhalten gleich bleiben soll? Hier bietet sich ein Structural Pattern an, das Proxy Pattern. Dieses Pattern beschreibt einen möglichen Weg, wie Aufrufe an ein Ziel weitergeleitet werden können. Dies ist genau das was wir benötigen. Zuerst jedoch das UML Diagramm, um einen ersten Überblick zu erlagen:



Wie zu sehen ist, wird der StrategyContext gegen einen Proxy ersetzt. Dieser erhielt zusätzlich zur eigentlichen DoWork-Methode eine weitere Überladung, dem ein ICommand übergeben werden kann. Damit kann entweder das Standardverhalten (welches durch die konkrete Strategy-Implementierung vorgegeben wird) oder aber ein beliebiges eigenes Verhalten ausgeführt werden. Dies wirft natürlich die Frage auf, warum man nicht trotzdem ausschließlich mit dem Strategy-Pattern arbeiten kann. Der Einfachheit wegen hat dieses Beispiel nur eine einzige Methode, diese Variante kommt jedoch erst bei mehreren angebotenen Methoden zu tragen.
Hier eine Beispielimplementierung (IStrategy und die konkreten Implementierungen haben sich nicht geändert und sind im obigen Sourcecode zu finden):
public interface ICommand
{
    void Execute();
}

public class CustomWorker1 : ICommand
{
    #region ICommand Members

    public void Execute()
    {
        Console.WriteLine("CustomWorker1");
    }

    #endregion
}

public class CustomWorker2 : ICommand
{
    #region ICommand Members

    public void Execute()
    {
        Console.WriteLine("CustomWorker2");
    }

    #endregion
}

public class StrategyProxy
{
    private IStrategy _usedStrategy;

    public StrategyProxy(IStrategy usedStrategy)
    {
        _usedStrategy = usedStrategy;
    }

    public void DoWork()
    {
        _usedStrategy.DoWork();
    }

    public void DoWork(ICommand customWorker)
    {
        customWorker.Execute();
    }
}

Fazit


Dieser Beitrag hat gezeigt, wie gesamte Verhalten innerhalb einer Anwendung einfach ausgetauscht werden können bzw. einen Weg aufgezeigt, wie dies auf Basis von Methoden realisiert werden kann. Wer dies nun über eine Konfigurationsdatei konfigurieren möchte, der kann sich beispielsweise einen Builder basteln, welcher die Konfiguration ausliest und die darin angegebenen Typen mit Hilfe der Klasse Activator instanziert, den Proxy mit den notwendigen Instanzen füllt und ihn anschließend fertig konfiguriert zurück liefert.
  4 Kommentare - 1787 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


UML Modellierung mit Visual Studio

03.10.07 - Entwicklung, Patterns, .NET, Visual Studio
Beitrag von Norbert Eder
 Wer auf der Suche nach einem UML Modelling-Tool für Visual Studio ist, dem sei eine weitere Variante durch tangible engineering geboten. Zusätzlich zum kostenpflichtigen Tool tangible architect 4.0 gibt es auch eine kostenlose Variante die folgende Diagramme unterstützt:
  • Case Diagrams
  • Component Diagrams
  • State Charts
  • Class Diagrams
  • Activity Diagrams
  • Persistent Object Models

Hier ein Screenshot:

Die erstellten Diagramme und Modelle sind mit der kostenpflichtigen Variante kompatibel.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Enterprise Library Contrib September 2007 verfügbar

02.10.07 - Entwicklung, Patterns, .NET, Allerlei
Beitrag von Norbert Eder
 Seit gestern ist die Enterprise Library Contrib September 2007 auf CodePlex verfügbar. Was genau ist die EntLib Contrib?

"EntLib Contrib is a community-developed library of extensions to the patterns & practices Enterprise Library."

Folgende Features sind enthalten:
  • Erweiterungen Data Access Application Block: Provider für MySql, SqLite und SqlEx
  • Erweiterungen Exception Handling Application Block: SqlException Wrap Handler
  • Erweiterungen Logging Application Block: LogParser
  • Erweiterungen Policy Injection Application Block: PostSharp4EntLib, neue Matching-Regeln und neue Call-Handler
  • Erweiterungen Validation Application Block: CollectionCountValidator, TypeValidator, ObjectValidator usw.


  1 Kommentar - 617 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Enterprise Library für Visual Studio 2008 geplant

29.08.07 - Entwicklung, Patterns, .NET, Visual Studio, Internet, Community
Beitrag von Norbert Eder
 Grigori Melnik schrieb in seinem Blog, dass das pattern & practices Team eine Version der Enterprise Library für Visual Studio 2008 (Orcas) plant. Dazu wurde eine Umfrage gestartet die 10 Fragen enthält. Dadurch soll in Erfahrung gebracht werden, welche Szenarios und darauf abzielt, zu erfahren, was den einzelnen Entwicklern an der EntLib fehlt.

Wer sich also einbringen möchte, der möge an der Umfrage teilnehmen, um ein möglichst großes Feedback für die Enterprise Library zu leisten, die ja tatsächlich sehr hilfreich für uns Entwickler ist.
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


[Enterprise Library] NLog Trace Listener im Eigenbau

19.06.07 - Entwicklung, Patterns, .NET, Grundlagen
Beitrag von Norbert Eder
 Eventuell hat sich der werte Leser die Enterprise Library [1] bereits einmal genauer angesehen. Wenn nicht, dann möchte ich zuerst ein paar einführende Worte loswerden.

Die Enterprise Library besteht aus sogenannten Blocks. Jeder Block besitzt eine eindeutige Aufgabe. Beispielsweise gibt es den Logging und den Exception Handling Block. Der Vorteil besteht darin, dass es mit Hilfe der Blocks das Verhalten über die Konfiguration (App.config, Web.config, ...) gesteuert werden kann. Beispiel:

Bei Exceptions stellt sich die Frage, wie denn diese im Spezialfall zu behandeln sind. Sind sie für den User sichtbar, sollen sie geloggt werden, oder gar der schlimmste Fall: soll sie nicht behandelt werden? Die geworfenen Exceptions werden an den Exception Handling Block übergeben, welcher durch eine Konfigurationsdatei entsprechend eingerichtet wird. Im Nachhinein kann nun festgelegt werden, was mit bestimmten Exceptions passieren soll. Ebenso verhält es sich mit den anderen Blocks.

Beim Logging verhält es sich gleich. Durch die Konfiguration kann festgelegt werden, wohin geloggt werden soll. Hier stehen unterschiedlichste Möglichkeiten bereit: EventLog, Datenbank, Email, Flat File usw. In unserem Beispiel möchten wir allerdings alle geworfenen Exceptions via NLog aufzeichnen.

Sehen wir uns dazu allerdings das Grundgerüst an. Das Beispiel läuft als Konsolenanwendung und Instanziiert eine Klasse DoSomethingClass, welche eine einzige Methode ThrowAnException besitzt:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;

namespace ExceptionLoggingDemo
{
    public class DoSomethingClass
    {
        public void ThrowAnException()
        {
            try
            {
                int i = 0;
                i = 1 / i;
            }
            catch (Exception ex)
            {
                ExceptionPolicy.HandleException(ex, "DemoException");
                throw ex;
            }
        }
    }
}

Die Konsolen-Anwendung selbst sieht folgendermaßen aus:
using System;
using System.Collections.Generic;
using System.Text;

namespace ExceptionLoggingDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            new DoSomethingClass().ThrowAnException();
            Console.WriteLine("Exception thrown");
            Console.ReadKey();
        }
    }
}

Nun binden wir die Enterprise Library ein bzw. die Teile davon, die wir tatsächlich benötigen. Dabei handelt es sich um folgende Libraries:

- Microsoft.Practices.EnterpriseLibrary.Common
- Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
- Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging
- Microsoft.Practices.EnterpriseLibrary.Logging

Bevor wir nun daran gehen, die Enterprise Library zu konfigurieren, erstellen müssen wir uns genauer ansehen, wie der Ablauf aussehen soll.

Grundsätzlich binden wir den Exception Handling Block ein. Dieser fängt unsere Ausnahmen auf. Hier müssen wir eine neue Exception Policy hinzufügen, damit unsere Exception auch aufgefangen wird (hier sind unterschiedliche Policies möglich und können somit auch unterschiedlich behandelt werden). Dieser Policy hängen wir einen Logging Block um. Hierbei ist schön zu sehen, dass die einzelnen Blocks sehr gut miteinander zusammenarbeiten. Durch diese Definition legen wir fest, dass auftretende Exceptions mitgeloggt werden sollen. Nun muss der Logging Block entsprechend konfiguriert werden. Es muss also angegeben werden, wohin die Daten geschrieben werden und wie diese formatiert werden sollen. Daraus ergeben sich zwei wichtige Bestandteile des Logging Blocks:

- Trace Listener
- Formatter

Der Trace Listener bekommt die zu schreibenden Daten übermittelt. Durch die Einbindung eines Formatters können diese Daten entsprechend formatiert werden, um besser lesbar bzw. einfacher zu verarbeiten sind. Als Formatter stehen ein TextFormatter und ein XmlFormatter zur Verfügung. Eigene Ableitungen sind an dieser Stelle natürlich möglich. Zudem wissen wir nun, an welcher Stelle wir ansetzen müssen: Wir benötigen einen CustomTraceListener.

Dazu wird ein eigenes Projekt erstellt, welches den benutzerdefinierten Listener enthalten wird. Nachfolgend der Sourcecode des Listeners:
[ConfigurationElementType(typeof(CustomTraceListenerData))]
public class NLogTraceListener : CustomTraceListener
{
    private LogLevel _logLevel = LogLevel.Info;

    public override void TraceData(
        TraceEventCache eventCache, 
        string source, 
        TraceEventType eventType, 
        int id, 
        object data)
    {
        if (data is LogEntry && this.Formatter != null)
        {
            switch (eventType)
            {
                case TraceEventType.Critical:
                    _logLevel = LogLevel.Fatal;
                    break;
                case TraceEventType.Error:
                    _logLevel = LogLevel.Error;
                    break;
                case TraceEventType.Information:
                    _logLevel = LogLevel.Info;
                    break;
                case TraceEventType.Warning:
                    _logLevel = LogLevel.Warn;
                    break;
                case TraceEventType.Verbose:
                    _logLevel = LogLevel.Debug;
                    break;
                default:
                    _logLevel = LogLevel.Info;
                    break;
            }

            this.WriteLine(this.Formatter.Format(data as LogEntry));
        }
        else
        {
            this.WriteLine(data.ToString());
        }
    }

    public override void Write(string message)
    {
        LogManager.GetCurrentClassLogger().Log(_logLevel, message);
    }

    public override void WriteLine(string message)
    {
        LogManager.GetCurrentClassLogger().Log(_logLevel, message);
    }
}

Insgesamt werden drei Methoden überschrieben (wobei ansich nur zwei Methoden tatsächlich benötigt werden):

- TraceData
- Write
- WriteLine

Wichtig sind zwei Dinge: Durch das Überschreiben der Methode TraceData kommen wir an den TraceEventType. Dieser gibt an, um welches Fehler-Level es sich handelt (Critical, Error, Warning, Info, ...). Dies benötigen wir, um es an NLog weitergeben zu können. Der Rest besteht ansich im Aufruf der Methode WriteLine unter Einbindung des zugewiesenen Formatters.

Schließlich muss noch eine App.config eingebunden werden. In dieser Konfigurations-Datei wird die Konfiguration der Enterprise-Library hinterlegt. Per rechter Maustaste auf die App.config kann die Enterprise Library Configuration gestartet werden, welche sich mittlerweile netterweise ins Visual Studio integriert. Zur Konfiguration müssen die Einträge für das Exception Handling und für das Logging hinzugefügt werden. Beim Logging verwenden wir unseren CustomTraceListener. Dazu ist eine Assembly anzugeben (unser separates Projekt). Zu beachten ist hier folgendes:



Die Enterprise Library kann sowohl signiert als auch unsigniert eingebundern werden. Wird die signierte Variante verwendet, muss auch die eigene Assembly signiert sein. Zudem muss (im Falle eines TraceListeners) die Klasse von CustomTraceListener abgeleitet sein und folgendes Attribut gesetzt haben:
[ConfigurationElementType(typeof(CustomTraceListenerData))]

Damit ist alles getan, um unseren eigenen TraceListener laden zu können.



Als Verbesserung könnte hier ein eigener Formatter erstellt werden, welcher die Daten in einer besser lesbaren Form in unseren NLogTraceListener schreibt.

Hier nun der Download des Beispiel-Projektes:
Download NLog Trace Listener Demo

[1] Download Enterprise Library 3.1 (May 2007)
  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Builder Pattern in C#

01.06.07 - Entwicklung, Patterns, .NET, Grundlagen
Beitrag von Norbert Eder
 Eine Diskussion mit Thomas veranlasste mich, einige Beispiele für ein Builder Pattern im .NET Framework zu suchen, zu beschreiben und selbst ein kleines Beispiel aus der Praxis zu liefern.

Einführung


Grundlegend zählt das Builder Pattern zu den Object Creational Patterns - es werden durch dieses Entwurfmuster also Objekte erstellt. Sinn und Zweck des Builder Patterns ist es, den Objekt-Erstellungsprozess von der Repräsentation des Objektes zu entkoppeln. Dadurch ist es möglich, mit demselben Erstellungsprozess unterschiedliche Repräsentationen erstellen zu können.

Beispiel aus dem .NET Framework


Ein sehr gutes Beispiel aus dem .NET Framework ist durch den DbConnectionStringBuilder geben. Dieser stellt die Basisklasse für stark typisierte Ableitungen dar (SqlConnectionStringBuilder etc.). Sehen wir uns an dieser Stelle einen kurzen Sourcecode an:
SqlConnectionStringBuilder connStringBuilder = 
  new SqlConnectionStringBuilder();
connStringBuilder.DataSource = "(local)";
connStringBuilder.InitialCatalog = "MyDatabase";
connStringBuilder.UserID = "username";
connStringBuilder.Password = "$password%";

SqlConnection sqlConn = 
  new SqlConnection(connStringBuilder.ToString());

Mit Hilfe des SqlConnectionStringBuilder ist es möglich, einfach einen syntaktisch korrekten ConnectionString zu erstellen, woraus in weiterer Folge ein Connection-Objekt gebildet werden kann.

Ein eigenes Beispiel


Nun sehen wir uns anhand eines eigenen Beispiels die Implementierung des Builder Patterns an. Grundlage bilden Adressdaten, die in einer XML-Datei vorliegen und importiert werden müssen. Den Part des Importierens ersparen wir uns. Die erstellten Objekte werden lediglich in eine Liste gelegt und anschließend per ToString ausgegeben.



Wie in der Abbildung zu sehen ist, besteht diese Implementierung des Builder-Patterns aus zwei wichtigen Teilen:

- AddressBuilder
- AddressParser

Der Parser selbst ist kein echter Bestandteil des ursprünglichen Builder-Patterns, wurde jedoch in diesem Beispiel zum Parsen der Adressdaten herangezogen und in das Pattern integriert, um die Zuständigkeiten der Objekte sauber zu trennen.
public class AddressParser : AParser
{
    AddressBuilder _builder = null;

    public AddressParser(IBuilder builder)
    {
        _builder = (AddressBuilder)builder;
    }

    public override void Parse(XmlNode data)
    {
        if (data != null && _builder != null)
        {
            _builder.ResetData();

            XmlNode xnStreet = data.SelectSingleNode("street");
            XmlNode xnStreetNumber = 
                data.SelectSingleNode("streetnumber");
            XmlNode xnZip = data.SelectSingleNode("zip");
            XmlNode xnCity = data.SelectSingleNode("city");
            XmlNode xnCountry = data.SelectSingleNode("country");

            if (xnStreet != null) 
                _builder.Street = xnStreet.InnerText;
            if (xnStreetNumber != null) 
                _builder.StreetNumber = xnStreetNumber.InnerText;
            int zip = 0;
            if (xnZip != null) 
                Int32.TryParse(xnZip.InnerText, out zip);
            _builder.Zip = zip;
            if (xnCity != null) 
                _builder.City = xnCity.InnerText;
            if (xnCountry != null) 
                _builder.Country = xnCountry.InnerText;                
        }
    }
}


Der AddressBuilder selbst besitzt nun die Aufgabe, das eigentliche Address-Objekt zu erstellen und zurück zu liefern.
public class AddressBuilder : ABuilder
{
    private string _street = null;
    private string _streetnumber = null;
    private int _zip = 0;
    private string _city = null;
    private string _country = null;

    public string Street
    {
        get { return this._street; }
        set { this._street = value; }
    }

    public string StreetNumber
    {
        get { return this._streetnumber; }
        set { this._streetnumber = value; }
    }

    public int Zip
    {
        get { return this._zip; }
        set { this._zip = value; }
    }

    public string City
    {
        get { return this._city; }
        set { this._city = value; }
    }

    public string Country
    {
        get { return this._country; }
        set { this._country = value; }
    }

    public void ResetData()
    {
        this._city = null;
        this._country = null;
        this._street = null;
        this._streetnumber = null;
        this._zip = 0;
    }

    public override BaseObject Build()
    {
        Address address = new Address();

        address.City = this._city;
        address.Country = this._country;
        address.Street = this._street;
        address.StreetNumber = this._streetnumber;
        address.Zip = this._zip;

        return address;
    }
}

Die Ausführung der gesamten Anwendung erfolgt durch nachfolgenden Sourcecode:
List<Address> addresses = new List<Address>();

XmlDocument doc = new XmlDocument();
doc.Load("addresslist.xml");

XmlNodeList xnlAddresses = doc.SelectNodes("/addresses/address");

if (xnlAddresses != null && xnlAddresses.Count > 0) 
{
    AddressBuilder builder = new AddressBuilder();
    AddressParser parser = new AddressParser(builder);

    foreach (XmlNode xnAddress in xnlAddresses) 
    {
        parser.Parse(xnAddress);
        Address address = (Address)builder.Build();
        addresses.Add(address);
    }
}

foreach (Address a in addresses)
{
    Console.WriteLine(a.ToString());
    Console.WriteLine("---------------------");
}

Console.Read();


Fazit


Über dieses Pattern können durch kleine Anpassungen unterschiedlichste Typen abgewickelt werden, was sich sehr schnell positiv auswirkt und zudem alle für die passende Aufgabe notwendigen Schritte trennt. Dadurch lassen sich diese entsprechend anpassen bzw. gänzlich ersetzen.

Download Builder Pattern Demo

  2 Kommentare - 716 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Enterprise Library 3.0 - April 2007 erschienen

10.04.07 - Entwicklung, Patterns, .NET, Allerlei
Beitrag von Norbert Eder
 Die Enterprise Library 3.0 - April 2007 ist verfügbar und kann heruntergeladen werden. Folgende Application Blocks sind verfügbar:

- Caching Application Block
- Cryptography Application Block
- Data Access Application Block
- Exception Handling Application Block
- Logging Application Block
- Policy Injection Application Block
- Security Application Block
- Validation Application Block

Weitere Änderungen betreffen unter anderem die Integration der Windows Communication Foundation (WCF).

Download Enterprise Library 3.0 - April 2007

  Kommentar hinzufügen   |  0 Trackbacks   |  Permalink  |  Trackback-URL



Zurück Weiter