Kategorien

Weblog Liste

Archiv

Administration

Sign In
 Sunday, January 20, 2008
Sunday, January 20, 2008 9:42:14 AM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [0]

Nachdem ich einen DC aufgesetzt habe, wollte ich natürlich auch die vorhandenden (virtuellen) Maschinen in die Domain aufnehmen. Bei Rechnern, welche mittels Installation von CD aufgesetzt wurden funktioniert dies auch wunderbar. Bei virtuellen Maschinen deren Installation auf Basis eine Images, ohne weitere Vorbereitungen/Maßnahmen, erfolgte, kommt es naturgemäß zu Problemen. Die Rechner besitzen alle die gleiche SID und das mag der DC nun überhaupt nicht.

Also mit dem Tool NewSid dem Rechner eine SID verpasst und dann funktioniert es auch mit dem Aufnehmen in die Domain. Soweit so gut.

Nach dem notwendigen Reboot und dem obligatorischen Blick in das Eventlog wird mir fast schwarz vor Augen. Da lächelt mich doch diese (siehe Bild) schöne Fehlermeldung an. Der Blick auf die Eigenschaften offenbarte mir folgenden Text:

Unable to get the private bytes memory limit for the W3WP process. The ASP.NET cache will be unable to limit its memory use, which may lead to a process restart.

Was bedeutet das denn nun schon wieder? Nach einigen Ursachenforschungen kam dann folgendes heraus; Natürlich hat sich auch die SID der Accounts geändert und das verursacht beim IIS Probleme, da im konkreten Fall die Gruppe IIS_WPG nicht mehr die nötigen Rechte auf die Applikationpools besitzt. Ist zwar komisch, da NETWORK SERVICE und die anderen Accounts in der Metabase des IIS korrekt eingetragen wurden aber was solls, dass klärt sich sicher auch noch auf. Abgesehen von den Leichen, die ehemaligen Accounts mit nun "ungültiger" SID, sowie der fehlenden Gruppe waren die Einstellungen der IIS Metabase korrekt.

Die Fehlerbehebung erfolgte dann in zwei Schritten.
  1. Entfernung der ungültigen Accounts aus der Metabase
  2. Hinzufügen von IIS_WPG mit den nötigen Rechten.
Beides kann mittels dem Script Metaacl.vbs durchgeführt werden. Der Aufruf zum Hinzufügen von IIS_WPG sieht dann folgendermaßen aus:

cscript Metaacl.vbs "IIS://LocalHost/W3SVC/AppPools" <RECHNER>\IIS_WPG U

Das U steht für die zu setzenden Rechte, in diesem Fall für "Unsecure properties read" und genau diese Einstellung ist nötig. Nach dieser Prozedur sah mein Eventlog, abgesehen von den Problemen welche sich ergeben wenn ein installierter Team Foundation Server in eine Domain gehoben wird, richtig gut aus. Doch dazu später mehr.

 Friday, March 02, 2007
Friday, March 02, 2007 3:33:31 PM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [1]

Warum dieser Beitrag

Exceptionhandling ist zum Teil eine Frage der Philosophie und der Einstellung. Einige behandeln Ausnahmen überhaupt nicht und andere Verfahren nach der catch all Philosophie. Beides ist suboptimal.

Wie sieht vernünftige Ausnahmebehandlung aus

Grundsätzlich verfahre ich immer nach folgendenden Merksätzen:

Merksatz 2.0
Behandle nur Ausnahmen, welche du auch lösen kannst.

Dieses Verfahren gaukelt dem Benutzer einer Bibliothek nicht vor, dass sie sich im Normbereich bewegt. Weiterhin kann er für seinen Anwendungsfall geeignete Lösungsstrategien implementieren. Handelt es sich nicht um eine Bibliothek, sondern um das eigentliche Programm, erleichtert es das Testen und das Ermitteln von Grenzbereichen, welches dann wiederrum die Ableitung von geeigneten Lösungsstrategien ermöglicht. Sowohl für die Bibliotheken als auch für die Anwendung.

Merksatz 2.1
Ausnahmen sind bei der Entwicklung eines Produktes notwendig und willkommen. Sie verbessern die Qualität des Produktes, wenn sie frühzeitig auftreten können/dürfen.


Macht eine Anwendung von Log-Mechanismen Gebrauch, so ist nichts dagegen einzuwenden, wenn Codeblöcke existieren, welche der catch all Philosophie folgen, um die Ausnahme zu protokollieren. Wichtig ist nur, dass der Fehler an übergeordnete Schichten weitergeleitet wird. Hierzu ein Beispiel:

try
{
...
}
catch( ... )
{
...
}
catch( Exception ex )
{
log.Error("Unbehandelte Ausnahme", ex );
throw;
}


Im konkreten Beispiel wurde log4net verwendet. Wichtig für das Thema ist jedoch, das der rethrow-Mechanismus von .NET verwendet wird.


Wann sollen eigene Ausnahmen erzeugt werden

Eigene Ausnahmen sind sinnvoll, wenn diese ausserhalb eines Exception-Blocks geworfen werden. Sie testen beispielsweise ob die Parameter eines Methodenaufrufs innerhalb eines gültigen Wertebereiches liegen. Dann können und sollten Sie bibliotheks- und programmspezifische Ausnahmen werfen. Gern darf hier von vorhandenen Exceptions abgeleitet werden. Im konkreten Fall und unter .NET von einer ArgumentException.

Das Erzeugen einer eigenen Ausnahme innerhalb eines Exceptionblocks sollte wohl überlegt sein. Es erzeugt auf seiten des Nutzers zusätzlichen Aufwand, auslesen der InnerException, und macht somit den Grund der Ausnahme nicht vollends klar. Es ermutigt den Entwickler, die Programmsteuerung auf Basis von Ausnahmen vorzunehmen.

Sollte man sich dennoch für eigene Exceptiontypen innerhalb eines Exceptionblocks entscheiden, so muß unbedingt die eigentliche Exception, als InnerException, weitergeleitet werden. Andernfalls kann eine Fehlersuche sehr aufwändig werden.

Fazit

Die Ausnahmebehandlung ist ein wichtiger Bestandteil der Softwareentwicklung. Es bietet sich an, zu Beginn des Entwicklungsprozesses, nur wenige Ausnahmen zu behandeln. Im Verlauf der  Entwicklung, durch Testen, werden die Gründe für Fehler klarer und können effektiver behandelt werden. Das Verstecken von  Fehlverhalten nützt niemanden etwas, sondern führt zu nicht nachvollziehbaren Programmverhalten. Im schlechtesten Fall nach der Auslieferung eines Produktes.

 Thursday, January 11, 2007
Thursday, January 11, 2007 8:13:30 AM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [0]

Um NAnt mitzuteilen, welche Version des .NET Frameworks beim Kompilieren verwendet werden soll, muss einfach nur das Property "nant.settings.currentframework" entsprechend gesetzt werden. Sollen die Sourcen mit Framework 1.1 übersetzt werden, so ist im Buildscript folgendes zu notieren:


<property name="nant.settings.currentframework" value="net-1.1" />


Das Framework kann allerdings auch über die Kommandozeile angegeben werden mittels:


nant.exe -t:net-1.1

 | 
 Tuesday, January 09, 2007
Tuesday, January 09, 2007 10:42:38 AM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [1]

Karsten Samaschke ruft mit dem Portal ASPXperts eine Community ins Leben, welche sich u.a. mit den Themen ASP.NET, XML und XML-Webdienste beschäftigt. Ein Bereich, welcher mich persönlich sehr interessiert, ist die Software-Architektur.

Es wird immer wichtiger, seinen Projekten eine strukturierte und durchdachte Architektur zu Grunde zu legen. Häufige Anforderungsänderungen können in Projekten ohne klar definierte Architektur schnell zu Chaos führen. Deshalb fällt es mir sehr positiv auf, dass ASPXperts diesem Thema einen eigenen Bereich spendiert.

Erwartungsgemäß sind derzeit wenige Inhalte zu finden, das Projekt befindet sich in der Enstehungsphase. Mit der Zeit werden sicher interessante und lehrreiche Beiträge aufschlagen.

Ich für meinen Teil habe mich heute registriert.

 Monday, January 08, 2007
Monday, January 08, 2007 8:51:05 AM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [1]

Wenn eine ConfigSection mittels


System.Configuration.ConfigurationManager.GetSection("section")


eingelesen wird, ruft .NET den für die Section angegebenen Handler auf. Der Handler, welcher vom Typ IConfigurationSectionHandler ist, gibt lt. Schnittstelle ein Objekt vom Typ object zurück. Soweit so gut.


Wer nun denkt, das beim nochmaligen Aufruf von ConfigurationManager.GetSection(...) der Handler wiederholt aufgerufen wird, liegt falsch. Der ConfigurationManager gibt das selbe Objekt wie beim ersten Aufruf zurück. Man sollte vermeiden, irgendwelche Verlaufsdaten in diesem Konfigurationsobjekt abzulegen. Es kann sonst passieren das Daten ungewollt, über Sessions hinweg, gemeinsam genutzt werden.


Merksatz 1.0:
Die Section dient der Konfiguration von Geschäftsklassen und ist nicht selbst die Geschäftsklasse.
:-)

 Tuesday, December 12, 2006
Tuesday, December 12, 2006 6:25:27 PM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [0]

Wie versprochen nun die Fortsetzung.

Um "beliebige" Objekte lesen und schreiben zu können, bediene ich mich der Serialisierungmöglichkeiten von .NET. Es werden zwei neue Methoden eingeführt GetObject<T> und SetObject<T>. Die Implementierung dieser Methoden sieht dann wie folgt aus.

// <summary>
/// Gets the object.
/// </summary>
/// <param name="node">The node.</param>
/// <returns></returns>
private static T GetObject<T>(XmlNode node)
{
T result = default(T);

    //restore object from node
XmlSerializer ser = new XmlSerializer(typeof(T));
result = (T)ser.Deserialize(new StringReader(node.OuterXml));

return result;
}

/// <summary>
/// Sets the object.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="value">The value.</param>
private static void SetObject<T>(XmlNode node, T value)
{
if(value == null)
{
return;
}

using(MemoryStream stream = new MemoryStream())
{
//serialize object
XmlSerializer ser = new XmlSerializer(typeof(T));
ser.Serialize(stream, value);

//reset read position
stream.Position = 0;

XmlElement newNode = null;

using(XmlReader reader = XmlReader.Create(stream))
{
//move to the content node.
reader.MoveToContent();
//create new element in node context
newNode = node.OwnerDocument.CreateElement(reader.Name);
//set content
newNode.InnerXml = reader.ReadInnerXml();
}

//append the new node
node.AppendChild(newNode);
}
}
Die beiden Methoden können natürlich auch public gemacht werden, damit sie explizit aufgerufen werden können. Diese Variante ist im Falle einer reinen Hilfsklasse für unterschiedlichste Projekte in der Tat auch vorzuziehen, da es die Verwendungsmöglichkeiten besser hervorhebt.

Im konkreten Beispiel habe ich sie einfach als zusätzlichen Aufruf in die Methoden SetValue<T> und GetValue<T> eingebaut. Irgendwie muß ich ja mein Versprechen erfüllen, mich von der IConvertible-Schnittstelle zu trennen. Nun noch kurz die geänderten Bereiche obiger Methoden, dann sollte ich meiner Verpflichtung nachgekommen sein. :-)

public static T GetValue<T>(XmlNode node, T @default)
{
    ...
    if(!IsConvertible(typeof(T)))
    {
        //get complex object
        result = GetObject<T>(node);
        result = result != null ? result : @default;
    }
    else
    {
        ...
    }
    ...
}

public static void SetValue<T>(XmlNode node, T value)
{
    ...
    if(!IsConvertible(typeof(T)))
    {
        SetObject<T>(node, value);
        return;
    }
    ...
}

Diese Lösung ist ganz klar ein erster Ansatz. Sie weist in dieser Ausprägung noch einige Lücken auf, welche ich nicht verschweigen möchte.
  • Das Löschen von Objektknoten ist in dieser Variante nicht möglich
  • Das Setzen eines komplexen Objektes führt immer zu einem anfügen. Durch die nicht vorhandene Objektidentität wird bei mehrfachen Aufrufen das Objekt auch mehrfach hinzugefügt.
  • Das unterschiedliche Verhalten von Lesen und Schreiben kann zu Irritationen führen.
  • Die XML-Serialisierung kann unter Umständen nicht erfolgreich sein, was dann einige Anforderungen an die Klassen stellt, um dies dennoch zu ermöglichen.

 Monday, December 11, 2006
Monday, December 11, 2006 7:59:19 PM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [0]

Es ist wirklich interessant welches Laufzeitverhalten .NET beim Vergleichen von Strings zeigt.

Die statische Methode CompareOrdinal des Typs String war, in einem nicht repräsentativen Test, 13mal schneller als sein Bruder seine Schwester Compare, wenn für Compare keine explizite StringComparison angegeben ist. Mit StringComparison.Ordinal lässt sich dann auch Compare dazu überreden, einen schnellen Vergleich durchzuführen.
Leider bezahlt man die verbesserte Performance durch den Verlust der Kulturabhängigkeit. Diese wird jedoch nicht für alle Strings zwingend benötigt.

Achtung: CompareTo ist zwar etwas besser als Compare, mit einem Faktor von 11 aber nicht wesentlich performanter.
Monday, December 11, 2006 9:26:23 AM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [0]

Nachdem mich am Donnerstag ein Virus dahin gerafft hat und ich das Bett hüten durfte, soll es heute wieder was neues geben.


Häufig hat man es mit XML-Daten zu tun, welche ausgelesen und bearbeitet werden sollen. Die enthaltenen Daten sind oft von einem bestimmten Typ. Die nachfolgende Hilfsklasse stellt Schnittstellen zur Verfügung, um die notwendigen Typumwandlungen zu kapseln. Diese Variante funktioniert nur mit Typen welche IConvertible implementieren. Eine Erweiterung, welche diese Einschränkung nicht besitzt, gibt es morgen. :)

using System.Xml;
using System;

namespace de.wwaitz.Utils.XmlHelper
{
public class NodeHelper
{
/// <summary>
/// Gets a value indicating whether T is convertible.
/// </summary>
/// <value>
///     <c>true</c> if T is convertible; otherwise, <c>false</c>.
/// </value>
public static bool IsConvertible(Type resultType)
{
return typeof(IConvertible).IsAssignableFrom(resultType);
}

/// <summary>
/// Gets the value.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="@default">The @default.</param>
/// <returns></returns>
public static T GetValue<T>(XmlNode node, T @default)
{
T result = @default;

if(node == null) {
//invalid node
return result;
}

if(!IsConvertible(typeof(T))) {
//cast is not possible
return result;
}

//get value
string toGet = node.InnerText;
//convert to T
result = (T)Convert.ChangeType(toGet, typeof(T));

return result;
}

/// <summary>
/// Sets the value.
/// </summary>
/// <param name="node">The node.</param>
/// <param name="value">The value.</param>
public static void SetValue<T>(XmlNode node, T value)
{
if(node == null) {
//invalid node
return;
}

if(!IsConvertible(typeof(T))) {
//cast is not possible
return;
}

string toSet = Convert.ToString(value);
node.InnerText = toSet;
}
}
}

Die Verwendung könnte dann wie folgt aussehen:

internal class Program
{
private static void Main(string[] args)
{
string xml = "<root><my-bool>true</my-bool></root>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);

bool myBool = NodeHelper.GetValue<bool>(doc.DocumentElement["my-bool"], false);
NodeHelper.SetValue<bool>(doc.DocumentElement["my-bool"], !myBool);
}
}
 Tuesday, December 05, 2006
Tuesday, December 05, 2006 7:10:02 PM (Mitteleuropäische Zeit, UTC+01:00) #
Comments [1]

Vor einiger Zeit hatte ich mal ein kleines Helferlein geschrieben, um Listen zu sortieren. Gut, das Problem lässt sich eleganter mit delegates lösen aber Spaß hat es trotzdem gemacht. :)

using System;
using System.Collections.Generic;
using System.Reflection;

namespace System.Collections.Generic
{
/// <summary>
/// Ein generischer Algorithmus zum sortieren von Listen.
/// </summary>
public class GenericSort<T> : IComparer<T>
{
private MethodInfo _method;

/// <summary>
/// Erzeugt eine Instanz des Typs
/// [invariant] Der übergebene Member kann eine Methode oder ein Property sein.
/// Handelt es sich um eine Methode, darf sie keinen Parameter
/// besitzen. der Rückgabetyp der Eigenschaft/Methode muß vom Typ
/// IComparable sein.
/// </summary>
/// <param name="member"></param>
public GenericSort(string member)
{
MemberInfo[] infos = typeof (T).GetMember(member);

foreach (MemberInfo info in infos)
{
if (info.MemberType == MemberTypes.Property)
{
if (Initialize((PropertyInfo) info))
break;
}
else if (info.MemberType == MemberTypes.Method)
{
if (Initialize((MethodInfo) info))
break;
}
}

if(_method == null)
throw new InvalidOperationException("Member does not match the class invariant");
}

/// <summary>
/// Initializes the specified info.
/// </summary>
/// <param name="info">The info.</param>
/// <returns></returns>
private bool Initialize(PropertyInfo info)
{
return Initialize(info.GetGetMethod());
}

/// <summary>
/// Initializes the specified info.
/// </summary>
/// <param name="info">The info.</param>
/// <returns></returns>
private bool Initialize(MethodInfo info)
{
if(info.GetParameters().Length > 0)
return false;

if(!typeof(IComparable).IsAssignableFrom(info.ReturnType))
return false;

_method = info;
return true;
}

/// <summary>
/// Compares two objects and returns a value indicating whether one is less than,
/// equal to, or greater than the other.

/// </summary>
/// <param name="x">The first object to compare.</param>
/// <param name="y">The second object to compare.</param>
/// <returns>
/// Value Condition Less than zerox is less than y.Zerox equals y.Greater
/// than zerox is greater than y.

/// </returns>
public int Compare(T x, T y)
{
return ((IComparable) _method.Invoke(x, null)).CompareTo(_method.Invoke(y, null));
}
}
}
Die Sortierung Liste mit Elementen des fiktiven Typs "SomeType" erfolgt dann einfach über nachfolgenden Aufruf:

public class Program
{
    public static void Main(string[] args)
    {
        List<SomeType> list = new List<SomeType>();

        //Code

        list.Sort(new GenericSort<SomeType>("Value"));
    }
}

class SomeType
{
    private int _value;

    public int Value
    {
        get { return _value; }
        set { _value = value; }
    }
}