code it

Martins Tech Blog

Entdeckungstour im SQL-Server 2008 (Teil 4) - der Geometry-Datentyp

Nachdem ich mich in Teil 3 der Entdeckungstour etwas näher mit dem Datentyp HierarchyId beschäftigt hab, möchte ich jetzt etwas auf den ebenfalls neuen Datentyp Geometry eingehen.

Auch der Geometry-Datentyp ist ein CLR-Datentyp, der in jeder Datenbank verfügbar ist. Er erlaubt die Darstellung von Daten im kartesischen Koordinatensystem und die Durchführung von Berechnungen mit diesen Daten.

Das möchte ich an einem Beispiel verdeutlichen:

 

In diesem Koordinatensystem befinden sich 2 geometrische Figuren. Ein Strahl mit den Endpunkten (0; 0), (100; 200) und ein Quadrat mit den Eckpunkten (-40; -40), (100; -40), (100; 100), und (-40, 100).

Die Deklaration einer Tabelle zur Speicherung der Daten ist recht trivial. Hierzu erzeugt man einfach eine Tabellenspalte vom Typ Geometry in die man die Daten schreibt.

Ich möchte hier allerdings auf die Möglichkeiten des Datentyps eingehen. Daher werden in den folgenden Beispielen nur Variablen dieses Datentyps erzeugt. Mit Hilfe der statischen Methode STGeomFromText kann aus einem normalsprachlichen Text (dem sogenannten "Well Known Text" = WKT) eine geometrische Instanz erzeugt werden. Im Beispiel ist das:

DECLARE @line geometry; 
SET @line = geometry ::STGeomFromText('LINESTRING (0 0, 100 200)',0)
DECLARE @rectangle geometry ; 
SET @rectangle = geometry ::STGeomFromText('POLYGON ((-40 -40, 100 -40, 100 100, -40 100, -40 -40))',0)

Wie man sieht, wird hier die Art der geometrischen Figur angegeben  und Punkte, die die Figur näher beschreiben. Das Quadrat ist verallgemeinert ein Polygon, daher ist der WKT hier POLYGON. Zu beachten ist bei Polygonen, dass Start- und Endpunkt übereinstimmen müssen.

Soweit ganz gut. Das Erzeugen und Speichern von Daten an sich ist recht langweilig. Was können die Methoden, die geometry zur Verfügung stellt?

STAsText

STAsText gibt den WKT für ein geometry-Objekt zurück.

Ok, das ist noch nichts weltbewegendes. Daher nun etwas Spannenderes...

STArea

Diese Funktion berechnet die Oberfläche von geometrischen Objekten. 

Im Fall der Linie ist die Oberfläche 0, aber das ist ehrlich gesagt auch nicht verwunderlich. Im Fall des Quadrats hat die Methode korrekt 140 * 140 = 19600 zurückgegeben. 

STIntersection

Mit STIntersection wird die Überschneidung zweier Objekte berechnet.

Das Ergebnis der Berechnung ist eine Linie mit den Endpunkten (0; 0) und (50; 100) - und genau das kann man auch sehr schön im Diagramm ablesen.

STLength

Ein letztes Beispiel an dieser Stelle soll STLength sein. Wie der Name schon vermuten lässt, kann damit die Länge bzw. der Umfang der Objekte berechnet werden.

Die Anwendungsfälle dafür können sehr vielfältig sein. Beispielsweise könnte hier die Entfernung zwischen Gebäuden oder Objekten berechnet werden.

Zusammengefasst ist dieser Datentyp für mich eher eine nette Spielerei. Auf Datenschicht-Ebene hatte ich bisher noch nie das Verlangen geometrische Figuren zu speichern und Berechnungen damit durchzuführen. Ich bin mir aber sicher, dass es Anwendungsmöglichkeiten für diesen Datentyp gibt. Und dieser Typ ist im Grunde auch Basis für einen weiteren neuen Datentyp - den Geography-Datentyp - auf den ich in einem späteren Post der Entdeckungstour etwas näher eingehen möchte.

Entdeckungstour im SQL-Server 2008 (Teil 3) - der HierarchyId-Datentyp

Im vorigen Post meiner Entdeckungstour ging es um neue Datentypen im Bereich Datum und Uhrzeit. Heute möchte ich mich einem weiteren interessanten Datentyp widmen: HierarchyId.

Hierarchische Daten

Möchte man hierarchische Daten ablegen - wie z.B. Organisationsstrukturen in Unternehmen, so war das Standard-Vorgehen dazu bisher, 2 Felder zu verwenden - zunächst die Id des eigentlichen Elements und in einem weiteren Feld die Id des Elternknotens.

Damit lassen sich Fragen wie: "Wer ist direkter Vorgesetzter des Mitarbeiters X?" oder "Welche Mitarbeiter sind direkt dem Vorgesetzten Y unterstellt?" sehr einfach und schnell beantworten. Umfangreichere Aussagen wie: "Was sind alle direkten und indirekten Mitarbeiter des Managers X?" oder "Wer ist Ebene-3-Manager des Mitarbeiters Y?" sind damit schon schwieriger zu ermitteln.

Nun kommt der HierarchyId-Datentyp ins Spiel. Mit ihm können solche Zusammenhänge dargestellt und einfach abgefragt werden. Als Besonderheit gilt es zu beachten, dass dieser Datentyp als CLR-Datentyp implementiert ist, der allerdings auch dann zur Verfügung steht, wenn CLR-Integration deaktiviert ist. Daraus resultierend leitet sich auch die Darstellung der Funktionen ab. Dazu aber später mehr. Zunächst möchte ich die oben gezeigte Struktur mit diesem Datentyp aufbauen. Dazu erstelle ich eine neue Tabelle.

Im nächsten Schritt wird diese mit Daten gefüllt.

Lässt man sich nun die Daten einmal anzeigen, ergibt sich folgendes Bild:

Man erkennt, dass die Daten vorliegen - allerdings ist OrgNode eine hexadezimale Darstellung, die nicht ganz so benutzerfreundlich ist. Dafür gibt es Abhilfe, denn die Methode ToString gibt die kanonische Darstellung des OrgNodes zurück:

Man erkennt nun recht gut, wie die hierarchischen Beziehungen sind.

Das war ja schonmal ganz interessant. Aber ich hatte eingangs ja erwähnt, dass damit nun alles ganz einfach wird. Dafür bietet es sich an, direkt einen Blick auf die Methoden des Datentyps zu werfen:

GetRoot()

Die (statische) Methode GetRoot() habe ich für das Füllen der Tabelle ja schon verwendet. Sie ermittelt den Root-Knoten einer Hierarchie.

GetAncestor()

child.GetAncestor(n) ermittelt den n-ten Vaterknoten eines Kindknotens. GetAncestor(1) würde also den direkt übergeordneten Knoten ermitteln.

Im Beispiel wird der Vorgesetzte des Vorgesetzten der Mitarbeitern Wendy Kahn ermittelt. Da sie sich auf der dritten Hierarchieebene befindet, ist das der CEO.

GetDescendant()

Auch parent.GetDescendant (child1, child2) habe ich schon im Beispiel verwendet. Dabei handelt es sich um eine sehr flexible Methode. Mit dieser ist es möglich sich sowohl Kinder eines gegebenen Knotens geben zu lassen als auch eine neue HierarchyId zwischen zwei Knoten erzeugen zu lassen. Dabei ist besonderes Augenmerk auf die Parameter zu legen:

parent child1 child2 Rückgabewert
NULL     NULL
nicht NULL NULL NULL erstes Kind von parent
nicht NULL nicht NULL NULL erstes Kind von parent, das größer als child1 ist
nicht NULL NULL nicht NULL erstes Kind von parent, das kleiner als child1 ist
nicht NULL nicht NULL nicht NULL erstes Kind von parent, das größer als child1 aber kleiner als child2 ist

In folgenden Fällen wird eine Exception geworfen:

  • Es sind Werte für child1 und child2 angegeben, diese sind aber keine Kindelemente von parent.
  • Es sind Werte für child1 und child2 angegeben, child1 ist aber größer als child2.

GetLevel()

Mittels node.GetLevel() wird die Tiefe ermittelt, in der sich der Knoten befindet.

IsDescendantOf

Mit der Methode parent.IsDescendantOf(child) wird ermittelt, ob ein Knoten ein übergeordneter Knoten eines anderen ist.

Dabei spielt es keine Rolle, auf welcher Ebene die Knoten untergeordnet sind. Ein IsDescendantOf für den parent Ken würde für alle Datensätze 1 zurückgeben. Im Beispiel wird gleich noch eine weitere Besonderheit sichtbar. Der parent wird selbst als child für sich selbst betrachtet.

Parse()

Die (statische) Methode Parse(input) gilt als das Gegenteil von ToString() und ermöglicht die Erzeugung einer HierarchyId durch Angabe der kanonischen Form.

GetReparentedValue()

Mittels der Methode node.GetReparentedValue(oldRoot, newRoot) können Knoten innerhalb der Struktur umgehängt werden. Eine Verwendung in SELECT-Statements erlaubt eine Vorschau, erst mit der Ausführung von UPDATE werden die Daten auch aktualisiert.

Beim UPDATE sollte man darauf achten, dass die neuen HierarchyIds auch weiterhin eindeutig sind und ggf. alle Kindknoten mit verschoben werden. 

Wenn ich einmal beim Thema Eindeutigkeit bin - das kann man am einfachsten mittels eines unique Index auf der OrgNode-Spalte erreichen.

Und wenn ich einmal bei dem Thema Index bin, will ich auch gleich noch einen Blick darauf werfen. Es gibt für diesen Datentyp nämlich unterschiedliche Arten zu indizieren und je nachdem, welche Art von Abfrage häufiger auf den Datenbestand ausgeführt wird, kann sowohl die eine als auch die andere Variante sinnvoll sein:

Tiefe zuerst (depth-first) 

Das ist die Standardsortierung. Dabei wird zuerst in die Tiefe gegangen bevor in die Breite gegangen wird. Die Sortierung der Elemente ist dann wie folgt: 


/1/ 
/1/1/ 
/1/2/ 
/2/ 
/2/1/ 
/2/2/ 
/2/3/ 
/3/ 
...

Breite zuerst (breadth-first) 

In gewissen Fällen ist es sinnvoll, bei der Indizierung erst alle Elemente einer Ebene zu indizieren und dann auf die nächste Ebene zu gehen: 

/
/1/ 
/2/ 
/3/ 
/1/1/ 
/1/2/ 
/2/1/ 
/2/2/ 
/2/3/ 
... 

Das Anlegen eines Depth-First-Index ist recht trivial - dazu indiziert man einfach eine bestehende HierarchyId-Spalte. 

Für die Anlage eines Breadth-First-Indexes benötigt man noch eine zusätzliche Spalte in der Tabelle, in der der Level persistiert und die dann in den Index mit einbezogen wird. 

Entdeckungstour im SQL-Server 2008 (Teil 2) - neue Datums- und Uhrzeitdatentypen

Im ersten Teil meiner Entdeckungstour im SQL Server 2008 ging es primär um neue Features, die Datenbankentwicklern das Leben leichter machen. Heute geht es weiter mit einigen neuen Datentypen, die mit dieser Version dazugekommen sind.

Datum und Uhrzeit

Bisher standen zur Speicherung von Datums- und Uhrzeitwerten die Datentypen datetime und smalldatetime zur Verfügung. Wie der Name schon sagt, kann mit diesen eine Kombination aus Datum und Uhrzeit dargestellt werden. Wollte man nur eine der beiden Komponenten speichern (z.B. Beginn- und Enduhrzeiten von Terminserien), so musste man einen der datetime-Datentypen verwenden. Die Nachteile liegen klar auf der Hand: es werden unnötige und unnütze Informationen gespeichert (z.B. Uhrzeiten, wo nur das Datum eine Rolle spielt); die Applikationslogik muss sicherstellen, dass Werte in wieder interpretierbarer Form abgelegt werden und es finden immer Konvertierungen statt, damit einzelne Komponenten extrahiert werden können. Ein typisches Beispiel für solche Konvertierungsverrenkungen hab ich in einem Post zum Thema "Datum aus datetime extrahieren" an dieser Stelle ja schon erläutert. Ein weiterer Nachteil sind fehlende Zeitzoneninformationen. Es wurde stur ein Datum gespeichert - nur die schreibende Applikation wusste, ob es sich dabei um lokale Zeit, UTC oder irgend eine andere Zeitzone handelt.

SQL Server 2008 bietet nun vier neue Datentypen, die in die Kategorie "Datum und Uhrzeit" passen:

  • date
  • time
  • datetime2
  • datetimeoffset

date

Mit dem Datentyp "date" kann man Datumswerte ohne die Uhrzeit im Bereich 01.01.1000 bis 31.12.9999 speichern. Der Speicherbedarf einer date-Variablen ist 3 Byte. 

 

time

Mit dem Datentyp "time" lassen sich Tageszeiten ohne Datumskomponente speichern. Dabei werden Werte im Bereich von 00:00:00.0000000 bis 23:59:59.9999999 (Stunden, Minuten, Sekunden, Sekundenbruchteile) mit einer Standardgenauigkeit von 7 Nachkommastellen unterstützt. Die Genauigkeit kann bei der Erstellung angegeben werden und hat direkt Einfluss auf den benötigten Speicherplatz. 

 

Dass im Beispiel nur die ersten 3 Nachkommastellen mit Werten belegt sind ergibt sich aus der Tatsache, dass GETDATE einen datetime-Wert zurückliefert, der nur auf 3 Nachkommastellen genau ist.

datetime2

Der Datentyp datetime2 stellt eine Erweiterung des Datentyps datetime auf den Datumsbereich 01.01.0001 bis 31.12.9999 mit einer Genauigkeit von 7 Nachkommastellen und einem Speicherbedarf von bis zu 8 Byte dar. Im Vergleich hat der Datentyp datetime einen Wertebereich von 01.01.1753 bis 31.12.9999 und eine Genauigkeit von 3 Nachkommastellen.

datetimeoffset

Alle bisherigen Datumsdatentypen haben Zeitzoneninformationen ignoriert. Der Datentyp datetimeoffset stellt im Grunde die Erweiterung des datetime2-Datentyps um Zeitzoneninformationen dar. Dadurch erhöht sich der Speicherbedarf auf 10 Byte. Im folgenden Beispiel wird ein datetimeoffset-Wert in Mitteleuropäischer Zeit erzeugt.

 

Lesen und Schreiben von XML-Dateien mit LINQ to XML

Nachdem ich mich in den letzten Einträgen zum Thema LINQ eher mit dem Thema LINQ to Objects beschäftigt habe, möchte ich nun auch das Thema LINQ to XML anschneiden, denn auch beim Zugriff auf XML-Daten bietet LINQ den ein oder anderen Vorteil.

Ohne LINQ erfolgt der Zugriff auf XML-Daten zumeist mit den Klassen XmlDocument, XmlElement, XmlAttribute aus den Namespace System.Xml. Zum direkten Zugriff eignet sich XPath und zum Lesen und Schreiben der Dateien verwendet man häufig XmlReader- und XmlWriter-Objekte.

Mit LINQ ändern sich die verwendeten Objekte. Nun stehen die Klassen XDocument und XElement aus dem Namespace System.Xml.Linq zur Verfügung.

Grundlage für die Beispiele soll eine XML-Datei mit Adressdaten (aus der AdventureWorks-Beispieldatenbank) sein. Diese hat folgenden Aufbau:

<?xml version="1.0" encoding="utf-8"?>
<Adressen>
   <Adresse>
       <Id>1</Id>
       <Name>Guy Gilbert</Name>
       <Address>7726 Driftwood Drive</Address>
       <PostalCode>98272</PostalCode>
       <City>Monroe</City>
       <Country>United States</Country>
       <Phone>320-555-0195</Phone>
       <EmailAddress>guy1@adventure-works.com</EmailAddress>
   </Adresse>
   <Adresse>
       <Id>2</Id>
       <Name>Kevin Brown</Name>
       <Address>7883 Missing Canyon Court</Address>
       <PostalCode>98201</PostalCode>
       <City>Everett</City>
       <Country>United States</Country>
       <Phone>150-555-0189</Phone>
       <EmailAddress>kevin0@adventure-works.com</EmailAddress>
   </Adresse>
</Adressen>

Schreiben von XML-Daten

Schreiben von XML-Daten im .NET 2.0-Style

Zunächst soll die Beispieldatei ohne Verwendung von LINQ erzeugt werden. Damit der Code etwas übersichtlicher wird, hab ich die Erzeugung eines einfachen Elementknotens mit Text in eine separate Funktion ausgelagert.

 

Die eigentliche Routine sieht dann wie im folgenden Beispiel aus:

 

Zunächst wird ein neues XmlDocument erzeugt. Diesem wird zunächst die Xml-Deklaration hinzugefügt. Im Anschluss daran wird der Hauptknoten erzeugt und diesem die beiden Unterknoten hinzugefügt. Das letzte Statement speichert das Dokument.

Schreiben von XML-Dateien mit LINQ

Das Ganze funktioniert auch mit LINQ und - wie ich finde - übersichtlicher und einfacher.

 

Im Konstruktor der Klasse XElement können beliebig viele andere Objekte angegeben werden, so dass schon hier die komplette Dokumentenstruktur aufgebaut werden kann. Mit entsprechenden Einrückungen im Quellcode bleibt es trotzdem übersichtlich. 

Lesen von XML-Daten 

Lesen von XML-Daten im .NET 2.0-Style

Aus der Datei sollen nun die Namen der Personen nach Postleitzahl sortiert ausgegeben werden - der Einfachheit halber auf der Konsole. Auch hierzu zunächst der Quellcode aus .NET 2.0.

 

Zunächst wird die XML-Datei als XmlDocument geladen. Über GetElementsByTagName können alle Elemente mit dem Tag-Namen "PostalCode" ermittelt werden. Diese werden dann in eine generische Liste hinzugefügt, um einfacher mit Hilfe von einer anonymen Methode sortieren zu können.

Das Schema vorausgesetzt kann dann mittels Zugriff auf den ParentNode der Name der zugehörigen Person ausgelesen und ausgegeben werden.

Lesen von XML-Dateien mit LINQ

Auch hier geht alles etwas eleganter und lesbarer mit LINQ.

 

Es wird ein neues XDocument erzeugt, das mittels der statischen Methode Load auch gleich mit Daten befüllt wird. Dann übernimmt eine einzige Linq-Anweisung die komplette Erfüllung der Anforderung. Sie ermittelt alle Namen aus den Adress-Elementen und sortiert diese nach Postleitzahl.

Ähnlich wie LINQ to Objects ist auch LINQ to XML recht einfach in der Handhabung und bringt schnell die gewünschten Ergebnisse.

Entdeckungstour im SQL-Server 2008 (Teil 1) - Zucker für Entwickler

Nachdem ich nun schon so viel über den SQL-Server 2008 gehört hab, hatte ich mich gestern dazu entschlossen, die Evaluierungsversion auch zu installieren. Dabei sind mir auch gleich ein paar schöne Neuerungen aufgefallen, die besonders die Datenbankentwickler unter uns freuen werden.

1. IntelliSense

Das überhaupt Beste von allem gleich zu Beginn: Die IDE hat jetzt IntelliSense. Was Visual Studio und die Office-Produkte schon lange können, steht nun auch in der IDE des SQL Servers zur Verfügung. 

 

Zugegebenermaßen gab es auch bisher schon Erweiterungen diverser Anbieter, ich hab mit einer integrierten Lösung ein besseres Gefühl als mit einer oben aufgesetzten. Wer sich etwas näher mit diesem Feature beschäftigen will und noch keine Erfahrung damit hat, findet unter Query -> IntelliSense Enabled den Button zum (de-)aktivieren und unter Edit -> IntelliSense die wichtigsten Shortcuts.

2. Table Valued Parameters (TVP)

Ein weiteres schönes Feature sind Table Valued Parameters. Damit ist es nun möglich, Wertlisten oder Wertepaare einfach an Stored Procedures (SPs) oder User Defined Functions (UDFs) zu übergeben. Bisher wurde das häufig über den Umweg über Zeichenketten gemacht, die zunächst auf der einen Seite erzeugt und dann in der Prozedur wieder mit mehr oder minder komplizierten Algorithmen in ihre Bestandteile zerlegt werden mussten. Mit Tabellenwertparametern, wie sie im Deutschen heißen, ist es nun möglich, diese Werte typisiert zu übergeben und so etwas mehr Stabilität zu erhalten. Ein kleines Beispiel zu TVPs findet sich im Techblog-Eintrag von Dorrit, so dass ich hier darauf verzichte.

3. Versüßter Umgang mit Variablen

Weitere sinnvolle Features, die ich in die Kategorie "syntaktischer Zucker" einsortieren will und die wohl bei Visual Studio abgeschaut wurden, zeigen sich beim Umgang mit Variablen.

Inline Deklaration

Wollte man bisher einer Variablen gleich bei der Initialisierung einen Wert zuweisen, so war das nicht möglich. Man musste immer 2 Statements schreiben.

DECLARE @maxCount int;
SET @maxCount = 5;

Mit SQL Server 2008 ist nun auch die Inline-Deklaration möglich:

DECLARE @maxCount int = 5;

Bis zur Version 2005 wurde der Versuch mit einer netten Fehlermeldung abgewiesen, die besagte, dass Standardwerte nicht zugewiesen werden können.

C-Syntax bei mathematischen Operationen

C- und C#-Entwickler haben sich schon an die verkürzte Syntax gewöhnt, wenn Variablen verändert werden sollen. Und auch T-SQL lässt jetzt Statements wie dieses zu: 

SET @maxCount += 5;

Schöne Weihnachten und guten Rutsch

So langsam aber sicher geht auch 2008 seinem Ende entgegen. Früher oder später trudeln die diversen Wünsche für Weihnachten und den Jahreswechsel ein und es beginnt die Zeit der Jahresrückblicke.

Mein Jahr war gespickt mit Herausforderungen und neuen Erkenntnissen und ich freue mich darauf, im nächsten Jahr wieder Herausforderungen zu begegnen und diese zu bewältigen.

Ich wünsche allen Lesern ein erfolgreiches Jahr 2009.

Erstellung eines Addins für SQL Server Management Studio

So wie man Addins für Visual Studio erzeugen kann, ist es auch möglich das SQL Server Management Studio (SSMS) mit eigenen Addins zu erweitern. Allerdings sind die Schritte und verwendbaren Objekte weitgehend undokumentiert. Aber es ist möglich und es funktioniert!

Erstellung eines neuen Addin-Projekts in Visual Studio

Die Erstellung eines SSMS-Addins ähnelt sehr dem Vorgehen der Erstellung eines Addins für Visual Studio. Man erzeugt zunächst ein neues Addin-Projekt (Other Project Types -> Extensibility -> Visual Studio Add-in).

Daraufhin öffnet sich ein Assistent. Nachdem man diesen erfolgreich durchlaufen hat, sollte das Projekt ungefähr wie im Bild dargestellt aussehen.

Registrierung des Addins

Die im Projekt enthaltenen ".AddIn"-Dateien können nun gelöscht werden. In Visual Studio 2005 und auch 2008 gibt es einen vergleichsweise neuen Weg, Addins ohne Eingriffe in die Windows Registry zu registrieren. SSMS scheint diesen allerdings nicht zu verwenden. Um das Addin zu registrieren muss also ein neuer Schlüssel in der Registry angelegt werden. Der richtige Platz dafür ist HKLM\SOFTWARE\Microsoft\Microsoft SQL Server\90\Tools\Shell\Addins bzw. HKCU\SOFTWARE\Microsoft\Microsoft SQL Server\90\Tools\Shell\Addins. Hier ist ein Schlüssel mit dem kompletten Namen der Klasse (inklusive Namespace) anzulegen, die das Interface IDTExtensibility2 implementiert. Wenn das Addin-Projekt mit dem Assistenten angelegt wurde, ist das die Klasse Connect. Unterhalb des Schlüssels ist nun ein DWORD-Wert mit dem Namen LoadBehavior und dem Wert 1 anzulegen. Diversen Ressourcen im englischsprachigen Netz zufolge kann man hier noch Werte bzgl. des Speicherortes (SatelliteDllName, SatelliteDllPath), der Addin-Beschreibung (ProductDescription, ProductName) oder des Verhaltens (CommandLineSafe, LoadBehavior) anlegen, allerdings hab ich im SSMS noch keinen Menüpunkt gefunden unter dem diese Informationen wieder sichtbar wären. Damit erübrigt sich meines Erachtens diese Mühe und der erzeugte Registry-Zweig sollte ungefähr wie folgt aussehen:

 

Achtung: Wird das SSMS jetzt gestartet, erscheint eine Fehlermeldung, dass ein Addin nicht korrekt geladen werden konnte und die Einstellung wird wieder zurückgesetzt und das Addin wird so lange nicht mehr geladen, bis die oben genannten Schritte wiederholt wurden.

COM-Sichtbarkeit

Das SSMS nutzt unter der Haube anscheinend noch sehr viel COM. Aus diesem Grund muss das Assembly "COM-visible" gemacht werden. Den entsprechenden Haken findet man im Assembly Information-Dialog.

 

Der Debugger sorgt von nun an beim Debuggen selbst für die Registrierung des Addins. Soll das Addin auf einem anderen Rechner zum Einsatz kommen, muss dafür gesorgt werden, dass die Assembly dort mit regasm.exe bzw. von einem Setup-Projekt registriert wird.

Coding 

Nun geht es an's Programmieren. Zusätzlich zu den Microsoft.SqlServer-Assemblies ist die  Assembly SqlWorkBench.Interfaces (C:\Program Files\Microsoft SQL Server\90\Tools\Binn\VSShell\Common7\IDE\SqlWorkbench.Interfaces.dll) für die Interaktion mit dem SSMS wichtig und es sollte auch auf diese eine Referenz hinzugefügt werden.

 

In der OnConnection-Methode des Addins kann man sich nun eine Referenz auf den Objekt-Explorer holen und damit einen Event wenn sich der ausgewählte Knoten ändert.

 

IObjectExplorerService objectExplorer = ServiceCache.GetObjectExplorer(); 
IObjectExplorerEventProvider provider = (IObjectExplorerEventProvider)objectExplorer
   .GetService(typeof(IObjectExplorerEventProvider));
provider.SelectionChanged +=new NodesChangedEventHandler(Provider_SelectionChanged);

Im so registierten Eventhandler kann man nun direkt in das Kontextmenü eingreifen und neue Einträge mit den gewünschten Funktionen hinzufügen.

 

Das hinzugefügte Objekt muss das Interface IWinformsMenuHandler implementieren und von ToolsMenuItemBase abgeleitet sein. Dabei stellt die Methode GetMenuItems die eigentlichen Kontextmenüeinträge zur Verfügung.

 

Debugging 

Zum Debuggen ändert man die Projekteigenschaften so, dass das SSMS gestartet wird:

 

WebPart-Tipps und Tricks: Umgang mit geschlossenen WebParts

Geschlossenen WebPart wieder in die Seite integrieren

Wenn man einen WebPart im SharePoint mit X schließt, so wird dieser trotzdem auf die Seite geladen und verbraucht Ressourcen - er wird lediglich nicht mehr angezeigt.

Um ihn wieder auf der Seite anzuzeigen sind folgende Schritte notwendig:

  1. Site Actions -> Edit Page auswählen
  2. Add a Web Part auswählen
  3. Advanced Web Part gallery and options auswählen
  4. Closed Web Parts auswählen
  5. Geschlossenen WebPart wieder auf die Seite ziehen

WebPart richtig löschen

Um den WebPart richtigerweise endgültig von der Seite zu löschen sind folgende Schritte notwendig:

  1. Site Actions -> Edit Page auswählen
  2. Edit auswählen
  3. Delete auswählen

Geschlossenen WebPart endgültig von der Seite löschen

Soll ein geschlossener WebPart nur gelöscht werden, kann dies auch über die Web Parts Maintenance Seite durchgeführt werden. Diese öffnet sich, wenn man an die URL der Seite "?contents=1" anfügt.

Ermittlung der Werte einer neu angelegten Tabellenzeile

Verfügt eine Tabelle über DEFAULT-Constraints für Spalten, die mit NOT NULL definiert wurden, werden für neu angelegte Zeilen diese DEFAULT-Werte in diesen Spalten angelegt. Nun müssen diese Vorgabewerte nicht zwingend fix sein, sondern können Rückgabewerte von Funktionen sein. Typisches Beispiel dafür sind die DEFAULT-Werte NEWID() oder NEWSEQUENTIALID().

Im Gegensatz zu einer Spaltendefinition mit identity-Werten können die so automatisch vergebenen Werte vom Typ uniqueidentifer allerdings nicht mit SELECT SCOPE_IDENTITY() oder SELECT @@IDENTITY zugreifen.

 

Um trotzdem zum Erfolg zu kommen, ist eine einfache Lösung, das OUTPUT-Schlüsselwort des INSERT-Befehls zu verwenden. Hier kann auf alle Spalten der virtuellen Tabellen inserted und deleted zugegriffen werden.