.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

Proxy-Pattern: Beschreibung und Beispiele

06.04.07 - Entwicklung, Patterns, .NET, Grundlagen
Beitrag von Norbert Eder
 Ein Proxy kann an vielen Stellen eingesetzt werden. Grundlegend handelt es sich dabei um einen Platzhalter für das tatsächlich aufzurufende Objekt. Das heißt, es leitet vom gleichen Interface ab, besitzt die gleichen Methoden, leitet aber alle Anfragen an das dahinterliegende Objekt weiter. Aber welchen Vorteil besitzt dieses Pattern nun?

Durch dieses Pattern ist es möglich, Änderungen und Prüfungen einzuführen, ohne das eigentliche Objekt abändern zu müssen. So können beispielsweise Sicherheitsprüfungen stattfinden (Protection Proxy). Dies bedeutet, dass das Proxy-Objekt zuerst überprüft, ob der Aufrufer über die notwendigen Rechte verfügt, bevor der Aufruf weitergeleitet und abgearbeitet wird. Weiters wird häufig ein Remote Proxy verwendet, der die Anfrage kodiert, an das echte Objekt sendet, die Antwort dekodiert und zurück gibt. Dieser Vorgang ist unter anderem dann interessant, wenn Application Domains erstellt werden sollen, die zur Laufzeit die Möglichkeit bieten sollen, entladen werden zu können. Schließlich wäre da noch Cache Proxies, die bestimmte Daten des eigentlichen Objektes cachen und so diverse Vorgänge beschleunigen können. Das sind jedoch nicht die einzigen Möglichkeiten. So gibt es noch Synchronization Proxies und viele weitere.

Ein einfaches Grundgerüst eines Proxies findet sich nachfolgend:
using System;
using System.Collections.Generic;
using System.Text;

namespace ProxyPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            CalculatorProxy proxy = new CalculatorProxy();
            Console.WriteLine(
                String.Format("12 + 17 = {0}", proxy.Add(12, 17))
                );
            Console.WriteLine(
                String.Format("12 - 17 = {0}", proxy.Sub(12, 17))
                );

            Console.Read();
        }
    }

    public interface ICalculator
    {
        decimal Add(decimal x, decimal y);
        decimal Sub(decimal x, decimal y);
    }

    public class Calculator : ICalculator
    {
        public decimal Add(decimal x, decimal y)
        {
            return x + y;
        }

        public decimal Sub(decimal x, decimal y)
        {
            return x - y;
        }
    }

    public class CalculatorProxy : ICalculator
    {
        private Calculator _calculator = new Calculator();

        public decimal Add(decimal x, decimal y)
        {
            return _calculator.Add(x, y);
        }

        public decimal Sub(decimal x, decimal y)
        {
            return _calculator.Sub(x, y);
        }
    }
}

Dies stellt die einfachste Variante eines Proxies dar. Ein vereinfachter Protection Proxy könnte beispielsweise so aussehen (Erweiterung des obigen Beispiels):
using System;
using System.Collections.Generic;
using System.Text;
using System.Security;

namespace ProxyPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                CalculatorProxy proxy = new CalculatorProxy("test2");
                Console.WriteLine(
                    String.Format("12 + 17 = {0}", proxy.Add(12, 17))
                    );
                Console.WriteLine(
                    String.Format("12 - 17 = {0}", proxy.Sub(12, 17))
                    );

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
    }

    public interface ICalculator
    {
        decimal Add(decimal x, decimal y);
        decimal Sub(decimal x, decimal y);
    }

    public class Calculator : ICalculator
    {
        public decimal Add(decimal x, decimal y)
        {
            return x + y;
        }

        public decimal Sub(decimal x, decimal y)
        {
            return x - y;
        }
    }

    public class CalculatorProxy : ICalculator
    {
        private Calculator _calculator = new Calculator();
        private string _password = "test";
        private string _givenPassword = null;

        public CalculatorProxy(string password)
        {
            this._givenPassword = password;
        }

        public decimal Add(decimal x, decimal y)
        {
            if (this._givenPassword == this._password)
                return _calculator.Add(x, y);
            else
                throw new SecurityException("Not allowed: Add");
        }

        public decimal Sub(decimal x, decimal y)
        {
            if (this._givenPassword == this._password)
                return _calculator.Sub(x, y);
            else
                throw new SecurityException("Not allowed: Sub");
        }
    }
}

Ich möchte nur darauf hinweisen, dass ein Protection Proxy entsprechend abgesichert sein sollte. Die obige Variante dient lediglich der Veranschaulichung und sollte so nicht verwendet werden.

  3 Kommentare - 2928 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


IsolatedStorage (Computerspeicher) verwalten

06.04.07 - .NET, Grundlagen, Datenverwaltung
Beitrag von Norbert Eder
 Wer Konfigurationen oder andere Informationen in den Isolated Storage schreibt, dem wird das Isolated Storage Tool (storeadm.exe) bekannt sein. Wem nicht, der sollte es sich genauer ansehen.

Es handelt sich dabei um eine Konsolen-Anwendung, mit der der Inhalt des IsolatedStorage angezeigt oder gelöscht werden kann. In manchen Fällen durchaus hilfreich.

Das Tool gibt es - wie auch den isolierten Speicher - seit .NET 2.0 und kann einfach über die Visual Studio 2005 Commandline aufgerufen werden.

Wer Informationen dazu benötigt, der sollte sich folgende Links genauer ansehen:
Einführung in die isolierte Speicherung
Szenarien für die isolierte Speicherung

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


Prozess-Output via C# anzeigen

06.04.07 - .NET, Grundlagen, Base Framework
Beitrag von Norbert Eder
 Hin und wieder kommt es dann doch einmal vor, dass via .NET andere Prozesse aufgerufen werden und deren Output auf die Standard-Ausgabe eingelesen werden soll. Dazu gibt es mehrere Möglichkeiten.

Die erste Variante liest alle Daten aus der Standard-Ausgabe ein, wenn der Prozess fertig abgelaufen ist:

ProcessStartInfo psi = new ProcessStartInfo("MyPathMyApp.exe");
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;

Process p = new Process();

p.StartInfo = psi;
p.Start();
StreamReader sr = p.StandardOutput;
p.WaitForExit();
string output = sr.ReadToEnd();
sr.Close();
Debug.WriteLine(output); 


Manchmal dauert der Prozess selbst länger und es ist wichtig, die Daten dann zu bekommen, wenn diese auf der Standard-Ausgabe geschrieben werden. Hierzu kann seit .NET 2.0 mit dem Event OutputDataReceived gearbeitet werden. Das sieht so aus:

private void StartProcess()
{
    ProcessStartInfo psi = new ProcessStartInfo("MyPathMyApp.exe");
    psi.RedirectStandardOutput = true;
    psi.UseShellExecute = false;
    Process p = new Process();

    p.OutputDataReceived += 
        new DataReceivedEventHandler(p_OutputDataReceived);
    p.StartInfo = psi;
    p.Start();
    p.BeginOutputReadLine();

    p.WaitForExit();            
}

void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
    Debug.WriteLine(e.Data);
}

In diesen Fällen werden die Informationen immer nur als Debug-Informationen geschrieben. Sie können aber natürlich auch anderweitig weiterverwendet werden.

Hier noch ein kleiner Hinweis: Dadurch wird alles abgefasst, was auf die Standard-Ausgabe geschrieben wird. Liegt also beispielsweise eine Windows-Forms-Anwendung vor, die zwecks Debugging-Informationen (siehe Visual Studio) viele Informationen via Console.WriteLine schreibt und das damit verteilt wird, können diese Informationen dadurch ebenfalls ausgelesen werden. Das sollte man sich in manchen Fällen schon ganz gut überlegen (siehe Debug.WriteLine).

  4 Kommentare - 10970 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Kopier-Konstruktor in C#

04.04.07 - .NET, Grundlagen
Beitrag von Norbert Eder
 Unter vielen Sprachen ist ein Kopier-Konstruktor standardmäßig für ein Objekt vorhanden. Bei C# ist dem nicht so. Nachfolgend möchte ich kurz zeigen, wie ein Kopier-Konstruktor in C# realisiert wird. Im Anschluss gibt es einen Hinweis von mir, warum ich persönlich Kopier-Konstruktoren nicht verwenden würde.

public class Car
{
    private int _maxSpeed = 200;
    private Color _color = Color.White;
    private Driver _driver = null;

    public int MaxSpeed
    {
        get { return this._maxSpeed; }
        set { this._maxSpeed = value; }
    }

    public Color Color 
    {
        get { return this._color; }
        set { this._color = value; }
    }

    public Driver Driver
    {
        get { return this._driver; }
        set { this._driver = value; }
    }

    public Car() { }

    public Car(Car car)
    {
        this._color = car.Color;
        this._maxSpeed = car.MaxSpeed;
        Driver d = new Driver();
        d.Firstname = car.Driver.Firstname;
        d.Lastname = car.Driver.Lastname;

        this._driver = d;
    }
}

Es gibt eine Überladung des Konstruktors, welche ein Objekt vom Typ Car entgegennimmt. Hierzu wird ein Deep Copy durchgeführt und die Daten entsprechend kopiert.

Dies klingt ja prinzipiell recht einfach und auch ganz gut. Was daran passt also nicht? Nun, man gehe von der Verwendung in einem Framework, welches anderen Entwicklern zur Verfügung gestellt wird aus, oder davon, dass sich der eigentliche Entwickler einige Wochen später nochmals an dieses Projekt setzt. Hier sieht er beispielsweise folgenden Code:

Car c = new Car();
Driver d = new Driver();
d.Firstname = "Norbert";
d.Lastname = "Eder";
c.MaxSpeed = 250;
c.Color = Color.Blue;
c.Driver = d;

Car c2 = new Car(c);

Auf den ersten Blick ist nicht ersichtlich, dass die Daten aus dem Objekt in das neu zu erstellende Objekt kopiert werden. Hierzu ist ein Blick in die Klasse notwendig. Ein Schritt, der nicht sein muss. Stattdessen empfiehlt es sich, das Interface ICloneable zu verwenden, oder mit MemberwiseClone zu arbeiten. Je nachdem welche Kopiermethode verwendet werden soll.

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


Einstellungen einfach speichern und laden

12.03.07 - .NET, Grundlagen, Base Framework
Beitrag von Norbert Eder
 Wer beispielsweise viele unterschiedliche Konfigurationen speichern muss (oder auch andere Daten), dem wird es nicht allzu einfach gemacht. Zum einen bietet sich eine Datenbank an (zusammen mit einem O/R Mapper). Wer keine Datenbank verwenden möchte (aus unterschiedlichsten Gründen), der muss sich anders behelfen. Serialisierung ist eine Möglichkeit. Da ich nun genau diesen Fall hatte, schrieb ich mir einen kleinen Manager der diese Aufgabe für mich übernimmt.

Implementiert ist die Klasse als Singleton und kann mit allen serialisierbaren Typen umgehen:

/// <summary>
/// Read and write Configuration files. Uses the binary formatter.
/// </summary>
public class ConfigurationManager
{
    #region Private Members

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

    private string _configPath = null;

    #endregion Private Members

    #region Properties

    /// <summary>
    /// ConfigurationPath is used to store the configuration
    /// </summary>
    public string ConfigurationPath
    {
        get { return this._configPath; }
        set { this._configPath = value; }
    }

    #endregion Properties

    #region ctor

    /// <summary>
    /// Private Constructor -> Singleton
    /// </summary>
    private ConfigurationManager() {}

    #endregion ctor

    #region Public Methods

    /// <summary>
    /// Creates a new instance if there wasn't already 
    /// one created, else the available instance will be 
    /// returned
    /// </summary>
    /// <returns>ConfigurationManager instance</returns>
    public static ConfigurationManager GetInstance()
    {
        lock (_lockObject)
        {
            if (_instance == null)
                _instance = new ConfigurationManager();
            return _instance;
        }
    }

    /// <summary>
    /// Read configuration. The typename is used as the filename
    /// </summary>
    /// <typeparam name="T">Any serializable type</typeparam>
    /// <param name="configuration">Serializable object</param>
    /// <returns>Given type T</returns>
    public T Read<T>(T configuration)
    {
        return Read<T>(configuration.GetType().Name);
    }

    /// <summary>
    /// Read configuration
    /// </summary>
    /// <typeparam name="T">Any serializable type</typeparam>
    /// <param name="configuration">Serializable object</param>
    /// <returns>Given type T</returns>
    public T Read<T>(string configuration)
    {
        if (this._configPath == null 
          :: !Directory.Exists(this._configPath))
            throw new Exception(
            String.Format(
            "ConfigurationManager: No valid path given: {0}", 
            this._configPath ?? "<not set>"));

        string filename = 
          Path.Combine(this._configPath, configuration) + ".config";

        if (!File.Exists(filename))
            throw new Exception(
            String.Format(
            "ConfigurationManager: Configuration {0} doesn't exist", 
            filename));

        BinaryFormatter bf = new BinaryFormatter();

        FileStream fs = new FileStream(filename, FileMode.Open);

        T tempObject = (T)bf.Deserialize(fs);

        fs.Close();

        return tempObject;
    }

    /// <summary>
    /// Write Configuration. The typename is used as the filename
    /// </summary>
    /// <typeparam name="T">Any serializable type</typeparam>
    /// <param name="configuration">Serializable object</param>
    public void Write<T>(T configuration)
    {
        this.Write(configuration, configuration.GetType().Name);
    }

    /// <summary>
    /// Write Configuration.
    /// </summary>
    /// <typeparam name="T">Any serializable type</typeparam>
    /// <param name="configuration">Serializable object</param>
    /// <param name="filename">Filename to be used</param>
    public void Write<T>(T configuration, string filename)
    {
        if (this._configPath == null 
          :: !Directory.Exists(this._configPath))
            throw new Exception(
            String.Format(
            "ConfigurationManager: No valid path given: {0}", 
            this._configPath ?? "<not set>"));

        string file = 
          Path.Combine(this._configPath, filename) + ".config";

        BinaryFormatter bf = new BinaryFormatter();

        FileStream fs = 
          new FileStream(file, FileMode.OpenOrCreate);

        bf.Serialize(fs, configuration);

        fs.Close();
    }

    #endregion Public Methods
}

Wie kann dieser Manager nun eingesetzt werden? Zuerst muss ein serialisierbares Objekt erstellt werden:

[Serializable]
public class Package
{
    public string PackagePath = null;
    public ArrayList DirectoryList = new ArrayList();
    public ArrayList FileTypeList = new ArrayList();
    public ArrayList FilesList = new ArrayList();
}

Diese Klasse ist serialisierbar und kann nun mit dem ConfigurationManager verwendet werden. (Der Name ConfigurationManager wird von mir für das Speichern von Konfigurationen verwendet. Grundlegend können alle serialisierbaren Typen damit gespeichert bzw. geladen werden.)

Und so geschieht in weiterer Folge der Aufruf:
ConfigurationManager cm = ConfigurationManager.GetInstance();
cm.ConfigurationPath = "MyPath";

Package pcp = new Package();
pcp.PackagePath = "Another Path";
cm.Write(pcp, "MyFile.config");

Package originalPackage = cm.Read("MyFile.config");

Anzumerken ist, dass der Pfad des ConfigurationManagers nur einmal gesetzt werden muss, also nicht bei jedem Aufruf (ausser die Konfigurationen werden in unterschiedlichen Verzeichnissen abgelegt).

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


Unbehandelte Ausnahmen (Unhandled Exceptions)

11.03.07 - .NET, Grundlagen, Base Framework
Beitrag von Norbert Eder
 Die Frage nach einer globalen Behandlung] von aufgetretenen Ausnahmen - und speziell von unbehandelten Ausnahmen) wird oft gestellt. Als mögliche Realisierung wird oft das folgende Bespiel gebracht (Ausgangspunkt ist hier eine Konsolenanwendung):

class Program
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.UnhandledException += 
            new UnhandledExceptionEventHandler
            (delegate(object o, UnhandledExceptionEventArgs 
            eventArgs)
        {
            Console.WriteLine("Unhandled Exception: " + 
                eventArgs.ExceptionObject);
        });
    }
}

Verschwiegen (ob bewußt oder aus Nichtwissenheit) wird meist, dass über diesen Weg zwar viele Ausnahmen abgefangen werden können, diese Möglichkeit jedoch nicht als "Exception-Handler" zu verwenden ist.

Warum nicht? Vorerst werden hiermit nicht behandelnden Exceptions mitgeteilt. Würde es sich um einen Handler handeln, wären die Exceptions nicht unbehandelt. (Achtung: Rekursions-Alarm!)

Weiters lassen sich einige Exceptions hiermit nicht behandeln, da sie via try-catch nicht abgefangen werden können (seit .NET 2.0: StackOverflowException). Zusätzlich ist zu beachten, dass es einen groben Unterschied macht, ob manuell (zwecks Testvorgang) eine StackOverflowException geworfen wird, oder tatsächlich eine echte auftritt. Im ersteren Fall könnte die Exception behandelt werden, im zweiteren Fall wird die Anwendung sofort terminiert.

Zusätzlich sind derartige Verhaltensweise idealerweise in einem Release-Build zu testen, da es Unterschiede zu Debug-Builds gibt und für gewöhnlich Release-Builds ausgeliefert werden.

Das bedeutet: Die einzige sinnvolle Einsatzvariante liegt darin, den aktuellen Anwendungsstatus zu speichern bzw. die nicht behandelnden Exceptions in eine Logdatei aufzuzeichnen. Denn die Anwendung wandert ohnehin über den Jordan (sofern eventArgs.IsTerminating wahr ist) ...

  7 Kommentare - 1872 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL


Transparente Steuerelemente mit C#

08.03.07 - .NET, Grundlagen, WPF
Beitrag von Norbert Eder
 Eigentlich ein alter Hut, aber ad hoc ist es mir heute auch nicht eingefallen wie eigene Steuerelemente transparent erstellt werden können. Der erste Versuch mit

this.Background = Color.Transparent;

scheiterte kläglich. Hier ein Lösungsweg:

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        cp.ExStyle := 0x20;
        return cp;
    }
}

protected override void OnPaintBackground
    (PaintEventArgs pevent)
{
    // do nothing in this case
}

protected override void OnMove(EventArgs e)
{
    RecreateHandle();
}

Das war es dann auch schon wieder.

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


Serviceorientierte Architekturen Grundlagen

03.03.07 - Entwicklung, Diskussionen, .NET, Grundlagen
Beitrag von Norbert Eder
 Serviceorientierte Architektur (SOA) ist wohl ein häufig gebrauchter Terminus in der heutigen Zeit. Wer sich beispielsweise näher mit der Windows Communication Foundation (WCF) beschäftigen möchte, sollte sich zuvor in die Grundlagen der SOA einarbeiten.

Wie die Bezeichnung vermuten läßt, besteht SOA aus lose gekoppelten Diensten, die jeweils bestimmte Aufgaben kapseln und unabhängig voneinander bezogen werden können. Ein Dienst wird von einem Service Provider angeboten, der Client nennt sich Service Consumer.

Ein einzelner Dienst stellt unterschiedliche Funktionen (Operationen) zur Verfügung, die von einem Consumer aufgerufen werden können. Dabei wird ein Service über eine Schnittstelle (Contract) definiert. Die Schnittstelle beschreibt also, welche Funktionalitäten und Nachrichten angeboten werden. Hierbei ist zusätzlich darauf zu achten, dass der Client die Implementierung der Funktionalität nicht kennt, da diese vom Service gekapselt wird. Dadurch ist es sehr einfach möglich, die Funktionalität selbst zu ändern, ohne Änderungen am Client (und ein damit verbundenes Rollout) vornehmen zu müssen.

Nun stellt sich die Frage, über welches Protokoll kommuniziert wird. Dies wird in sogenannten Policies festgelegt.

Wie erfolgt nun der Ablauf der Kommunikation?

Die Kommunikation erfolgt prinzipiell durch das Versenden und Empfangen von Nachrichten. Diese enthalten Daten und keine Objekte. Der Austausch erfolgt über sogenannte Endpoints die vom jeweiligen Service bereitgestellt werden. Ein Endpoint besteht aus drei Teilen:

- Adresse
- Binding
- Kontrakt

Die Adresse definiert, wo der Endpoint zu finden ist. Das Binding beschreibt, wie der Endpoint aufgerufen wird und der Kontrakt (wie bereits weiter oben beschrieben) definiert welche Operationen angeboten werden.

Weitere Informationen zu diesem Thema können unter folgenden Links gefunden werden:

SOA - Wikipedia
Service Oriented Architecture - Microsoft

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


Einführung in die Serialisierung unter .NET

23.02.07 - .NET, Grundlagen
Beitrag von Norbert Eder
 Serialisierung wird verwendet, um Objekte zu persistieren (speichern) oder diese in eine andere Form zu bringen, zur leichteren Übertragung zu anderen Systemen.

Zum letzten Livecast zum Thema .NET Serialisierung gibt es einen Online-Artikel, der eine Einführung in dieses Thema bietet und zusätzlich einfache Beispiele zeigt.

.NET Serialisierung - Eine Einführung

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


Livecast-Mitschnitt zum Thema Serialisierung verfügbar

21.02.07 - .NET, Grundlagen, Allerlei
Beitrag von Norbert Eder
 Auf .NET Casts steht ab sofort der Mitschnitt des 5. Livecast von .NET Casts zum Thema .NET Serialisierung zum Download bereit.

Neben einer Einführung in das Thema findet sich eine angeregte Diskussion zum Thema.

  Kommentar hinzufügen - 3 mal angesehen   |  0 Trackbacks   |  Permalink  |  Trackback-URL



Zurück Weiter