Monthly Archive for Juni, 2009

TypeConversions – Oder: Wer bist du denn?

Wir haben eine eigene Klasse welche unseren Datentyp darstellt. Diese möchten wir jetzt mit ein paar Typkonvertierungen versehen, allerdings nicht über Extension methods oder andere ausgelagerte Funktionen. Unser Datentyp sieht wie folgt aus:

class MyDataType
{
	public string Wert { get; set; }
}

Der Einfachheit halber nur eine simple Property. Normalerweise hat man ja sehr viele Unterschiedliche Properties in seinem Datentyp. Wir möchten jetzt daraus z.B. einen SecureString machen, oder auch einfach den Wert loggen. Man könnte jetzt natürlich einfach schreiben:

MyDataType myTypeInstance = new MyDataType() { Wert = "hallo" };

Log(myTypeInstance.Wert);

Wobei unsere Logfunktion einen string-Parameter entgegennimmt der dann – wo auch immer – protokolliert wird.

Diese Verwendung ist aber spätestens bei komplexeren Datentypen einfach nur unhandlich. Deshalb wollen wir einfach schreiben können:

Log(myTypeInstance);

oder um daraus einen SecureString zu machen:

SecureString mySecString = (SecureString)myTypeInstance;

Die meisten Implementierungen (die ich bisher gesehen habe) setzen auf eine extra ausgelagerte Funktion oder Extension methods. Die ausgelagerten Funktionen haben einen Nachteil: Sie konvertieren einfach mal alles was man reinwirft. Dieses Verhalten haben wir aber eigentlich nicht bezweckt, oder wir wollen das einfach nicht, da wir ja beim Zeitpunkt des Codens die Verwendungsmöglichkeiten einfach nicht alle im Überblick haben kann oder auch nicht testen kann.
Gehen wir also über Extension methods. OK – damit haben wir im Idealfall schonmal den Basistyp eingegrenzt. Nachteil allerdings hier: Extension methods funktionieren erst ab .Net 3.5… Also stehen wir wieder am Anfang.

Wie gehen wir also nun an die Sache ran?
Die Lösung ist liegt auf der Hand und ist eigentlich so simpel, das es mich immer wieder verwundert das es so selten eingesetzt wird:
Innerhalb unseres Datentyps definieren wir zwei neue statische Funktionen zur Typumwandlung hinzu:

public static implicit operator string(MyDataType toConvert) { }

public static explicit operator SecureString(MyDataType toConvert) { }

Diese beiden Funktionen führen nun unsere gewünschten impliziten wie expliziten Konvertierungen aus.
Wir füllen diese beiden Methoden also wie folgt auf:

public static implicit operator string(MyDataType toConvert)
{
	return toConvert.Wert;
}
public static explicit operator SecureString(MyDataType toConvert)
{
	SecureString mySecureString = new SecureString();
        foreach (char item in toConvert.Wert)
	{
		mySecureString.AppendChar(item);
	}
	return mySecureString;
}

Das heißt, dadurch gewinnen wir saubereren Code und klarere Strukturen, da die Umwandlungen direkt innerhalb der Typen definiert werden können. Sie sind also genau da, wo sie auch hingehören.
Das komplette Testprogamm hier:

class MyDataType
{
	public string Wert { get; set; }
	public static implicit operator string(MyDataType toConvert)
	{
		return toConvert.Wert;
	}
	public static explicit operator SecureString(MyDataType toConvert)
	{
		SecureString mySecureString = new SecureString();
		foreach (char item in toConvert.Wert)
		{
			mySecureString.AppendChar(item);
		}
		return mySecureString;
	}
}

class Program
{
	static void Main(string[] args)
	{
		MyDataType myTypeInstance = new MyDataType() { Wert = "hallo" };
		Log(myTypeInstance);
		SecureString mySecString = (SecureString)myTypeInstance;
		Log(mySecString.ToString());
		Console.Read();
	}
	static void Log(string message)
	{
		Console.WriteLine(message);
	}
}
Kick it on dotnet-kicks.de

To yield or not to yield

Das yield Keyword ist ein in C# häufig unterschätztes Keyword. Die MSDN – bzw. die C# Spezifikation – sagt dazu:

Def: Wird in einem Iterator-Block verwendet, um für das Enumerationsobjekt einen Wert zu liefern oder um das Ende der Iteration zu signalisieren.
Der Ausdruck wird ausgewertet und als ein Wert an das Enumerationsobjekt zurückgegeben. Der expression muss implizit in den yield-Typ des Iterators konvertierbar sein.
Die yield-Anweisung kann nur innerhalb eines iterator-Blocks stehen, der als Rumpf einer Methode, eines Operators oder eines Accessors verwendet wird. Der Rumpf solcher Methoden, Operatoren oder Accessoren unterliegt folgenden Einschränkungen:

- Unsichere Blöcke werden nicht zugelassen.
- Parameter für die Methode, den Operator oder den Accessor dürfen nicht ref oder out sein.

Eine yield-Anweisung darf nicht in einer anonymen Methode stehen. Weitere Informationen finden Sie unter Anonyme Methoden (C#-Programmierhandbuch).
Bei Verwendung mit expression darf eine yield return-Anweisung nicht in einem catch-Block oder einem try-Block stehen, der mehr als eine catch-Klausel enthält.

Wie sieht das jetzt in der Praxis aus?
Das Beispiel in der MSDN ist jetzt nicht besonders praxistauglich, daher hier mal eine andere Verwendungsweise:

class Program
    {
        static void Main(string[] args)
        {
            string searchedCRMId = "de7ea510-8e55-de11-a210-0017084f5232";
            Console.WriteLine("Starting to search for user with CRM_ID '{0}'...", searchedCRMId);
            foreach (UserProfile item in GetProfiles("http://your_ssp_host/"))
            {
                string currentID = Convert.ToString(item["CRMUserID"].Value);
                if (string.IsNullOrEmpty(currentID))
                    continue;

if (currentID.Equals(searchedCRMId, StringComparison.CurrentCultureIgnoreCase))
                {
                    Console.WriteLine("Found searched user!\nAccountname: {0}", item[PropertyConstants.AccountName].Value);
                    break;
                }
            }
            Console.WriteLine("Done");
            Console.ReadLine();
        }

static IEnumerable<UserProfile> GetProfiles(string SharedServiceProviderUrl)
        {
            SPWebApplication webapp = SPWebApplication.Lookup(new Uri(SharedServiceProviderUrl));
            UserProfileManager upm = new UserProfileManager(ServerContext.GetContext(webapp));
            foreach (UserProfile profile in upm)
            {
                yield return profile;
            }
        }
    }

Diese ConsoleApplication iteriert über alle vorhandenen UserProfile und vergleicht eine CustomProperty mit einem Suchwert. In der GetProfiles() Methode wird allerdings mittels “yield return profile” das aktuelle Profil aus der Auflistung zurückgegeben und die übergeordnete foreach-Schleife weiter bearbeitet. Das spart zum einen Performance und zum anderen Speicher.
Zu beachten ist dabei allerdings, das bei folgender Verwendung immer das erste Item “verschwindet”:

IEnumerable<UserProfile> profileList = GetProfiles("http://your_ssp_host/");
foreach (UserProfile item in profileList)
{
 string currentID = Convert.ToString(item["CRMUserID"].Value);
        if (string.IsNullOrEmpty(currentID))
         continue;

if (currentID.Equals(searchedCRMId, StringComparison.CurrentCultureIgnoreCase))
        {
         Console.WriteLine("Found searched user!\nAccountname: {0}", item[PropertyConstants.AccountName].Value);
                break;
        }
}

Die Ursache ist recht einfach:

Typische Implementation Implementation mit yield
1. Funktionsaufruf 1.Funktionsaufruf
2.Funktion wird ausgeführt und gibt eine Liste zurück 2.Aufrufer fordert ein Item an
3.Aufrufer arbeitet mit der Liste 3.Nächstes Item aus der Auflistung wird zurückgegeben
  4.Sprung zu 2. solange wie der Aufrufer kein Item mehr haben will (break im Caller) oder keine Items mehr vorhanden sind

Was man letztendlich verwendet ist natürlich immer von der Anforderung abhängig. :)

Kick it on dotnet-kicks.de

=(2,02095)^36 €

In etwa so viel haben Italienische Beamter an der Schweizer Grenze bei zwei Männern beschlagnamt, wie der Spiegel berichtet.

Sachen gibts…  :)

Kick it on dotnet-kicks.de

The following error was encountered while reading module ‘Microsoft.SharePoint’: Could not resolve type: ObjectModel.

Bei Projekten mit einer Referenz auf die “Microsoft.SharePoint.dll” laufen keine Code Metric Analysen. Der Grund ist nach einer kurzen Google-Recherche auch schnell gefunden: Ein Bug im Visual Studio 2008.
Ein Workaround gibts auch dazu: Man muss lediglich eine Referenz auf die “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\Microsoft.SharePoint.Security.dll” einbinden, dann klappts auch mit der Analyse.

Kick it on dotnet-kicks.de

SharePoint: Alle Sharedservice Provider ermitteln

Ab und an kommt mal wieder die Anforderung einen Timerjob an die Webapplication des Sharedserviceproviders anzuhängen. Bei einem SSP ist das auf den ersten Blick auch kein Problem: Entweder die URL als Konfigurationseinstellung verdraten oder über eine Settingspage in der Zentraladministration konfigurierbar machen. Spätestens wenn der Kunde aber mehr als einen SSP im Einsatz hat, und der Timerjob an alle SSPs angehängt werden soll, steht man vor einem Problem: Entweder immer wieder Einstellungen hinzufügen oder man überlegt sich eine generelle Lösung.

Jeder SSP verfügt über eine Administrationsoberfläche die mit einer bestimmten Sitedefinition erzeugt wurde. Diese findet man im 12er Hive unter TEMPLATE\SiteTemplates\OSRV. Wie kann man sich nun eine Liste aller vorhandenen SSPs erzeugen? Zum Beispiel mit folgender Funktion:

        public static List GetSharedServiceProviders()
        {
            List result = new List();
            SPFarm farm = SPFarm.Local;
            SPWebService service =
                farm.Services.GetValue();
            foreach (SPWebApplication webApp in
                service.WebApplications)
            {
                if (webApp.Sites.Count == 0)
                    continue;
                using (SPSite rootsite = webApp.Sites[0])
                {
                    if (rootsite.AllWebs.Count == 0)
                        continue;
                    using (SPWeb rootweb = rootsite.RootWeb)
                    {
                        if (rootweb.WebTemplateId == 40)
                            result.Add(rootsite.Url);
                    }
                }
            }
            return result;
        }

Eigentlich ist es simpel: Man holt sich aus der Farm alle WebApplications, öffnet die Rootsite und prüft mit welchem SiteTemplate das Rootweb angelegt wurde. Ist die TemplateID gleich der TemplateID der SSP Sitedefinition, dann ist es ein Sharedservice Provider.

Kick it on dotnet-kicks.de