SQL Server CLR Integration

Der einfachste Weg verwalteten Code (Managed Code – .NET Runtime – CLR) und SQL Server zu „verheiraten“ ist die Entwicklungsumgebung Visual Studio (2005 – 2010) zu verwenden. Visual Studio vereinfacht die Entwicklung drastisch, z.B. durch automatisierte Deployment Prozesse. Trotzdem werden wir den Prozess hier „per Hand“ erklären. Natürlich funktioniert dieser Weg auch mit jeder beliebigen .NET Entwicklungsumgebung.

Für jedes unterstützte Stück „Managed Code“ existieren im Namespace Microsoft.SqlServer.Server entsprechende Attribute welche Methoden für den SQL Server verfügbar machen. Dies sind im folgenden:

SqlProcedure 
SqlFunction
SqlUserDefinedAggregate
SqlUserDefinedType
SqlMethod

Auf die zwei häufigsten dieser Attribute möchte ich im folgenden nun kurz eingehen.

SqlProcedure

Die am meisten verwendeten – und nützlichsten – Features in eigenen Projekten sind wahrscheinlich Stored Procedures. Da diese aber meist in T-SQL entwickelt werden lassen sich damit meist nur schwierig komplexere Aufgaben abbilden. Zum Glück lassen sich Stored Procedures auch mit C# (oder einer anderen .NET kompatiblen Sprache) entwickeln. Jedoch gibt es gewisse Voraussetzungen für das entwickeln von „Managed Stored Procedures“.

  • Die enthaltende Klasse muss public sein
  • Die entsprechende Methode muss public static sein

Das sind alle Anforderungen. Klingt doch gar nicht mal so schlimm, oder? 😉 Um das ganze noch einmal zu verdeutlichen, hier ein Beispiel:

using System;
using Microsoft.SqlServer.Server;

namespace abapguru.SqlServer {
    public class SqlServerIntegration {
        [SqlProcedure("spDoSomething")]
        public static void DoSomething() {
            // Hier passiert was...
        }
    }
}

Hier gibt es also zu „normalen“ statischen Methoden kaum unterschiede. Das SqlProcedure Attribute macht die Methode DoSomething für den SQL Server zugreifbar. Der einzige unterstützte Parameter ist ein String der einen alternativen Namen für die Stored Procedure angibt. Im Normalfall ist dieser immer gleich dem Namen der Methode.

SqlFunction

Funktionen für den SQL Server zu erstellen ist fast so einfach wie Stored Procedures, mit dem einen unterschied dass die Methode einen Rückgabewert benötigt.

using System;
using Microsoft.SqlServer.Server;

namespace abapguru.SqlServer {
    public class SqlServerIntegration {
        [SqlFunction()]
        public static int CalculateSomething(int x, int y) {
            return (x + y);
        }
    }
}

Das Attribute SqlFunction unterstützt folgende Parameter:

  • DataAccess
    Bestimmt, ob für die Berechnung auf Daten zugegriffen werden muss oder ob die Berechnung autonom durchgeführt werden kann
  • IsDeterministic
    Bestimmt, ob die Funktion immer den selben Wert zurückgibt, unabhängig vom Status des SQL Servers
  • IsPrecise
    Bestimmt, ob das Ergebnis „wissenschaftlich präzise“ ist (z.B. für mehrdimensionale Berechnungen nötig)
  • Name
    Bestimmt den Namen der Funktion innerhalb des SQL Servers

Assembly in SQL Server einbinden

Angenommen wir haben nun eine fertige Assembly. Um diese innerhalb des SQL Servers verwenden zu können musse diese nur noch installiert eingebunden werden. Dies wird über einen T-SQL Befehl bewerkstelligt.

CREATE ASSEMBLY <NAME>
FROM <PATH>

Beispiel

CREATE ASSEMBLY abapguru.SqlServer
FROM 'C:\Assemblies\abapguru.SqlServer.dll'

Ich denke die Erklärung der Syntax kann ich mir sparen – das ist jedermann logisch oder 🙂

Eine Anmerkung noch: Standardmäßig wird die Assembly mit der „Berechtigung“ installiert, nur „safe Code“ auszuführen. Die CLR Integration hat die Notation von drei verschiedenen Ausführungsberechtigungen; diese werden auf Assemblyebene gesetzt.

  • SAFE
    Nur CLR ist ausführbar. Kein Zugriff auf externe Resourcen
  • EXTERNAL
    Zugriff auf externe Daten ist erlaubt. Darunter fallen z.B. Dateisysteme, Eventlog, Netzwerk. Kein Zugriff auf „unsafe Code“ or „interop code“.
  • UNSAFE
    Keine Limitation, somit Zugriff auf alle Resourcen erlaubt

Diese Ausführungsberechtigungen können beim Importieren der Assembly mit Hilfe des WITH PERMISSION_SET Zusatzes angegeben werden.

CREATE ASSEMBLY <NAME>
FROM <PATH>
WITH PERMISSION_SET=(SAFE|EXTERNAL|UNSAFE)

Ähnlich wie alle DDL Befehle gibt es analog zu CREATE natürlich auch DROP und ALTER. Hier verweise ich allerdings auf die MSDN Dokumentation 😉

Managed Code auf T-SQL mappen

Nachdem die Assembly nun dem SQL Server bekannt ist benötigen wir nur mehr einen letzten, aber wichtigen Schritt. Die in CLR Codierten Funktionen und Stored Procedures müssen nun noch auf äquivalente T-SQL Befehle „gemappt“ werden. Dazu benötigen wir (schon wieder) einige DDL Befehle. Einige Teile davon dürften aber durchaus dem ein oder anderen bekannt sein, der schon seine Erfahrungen mit Stored Procedures gemacht hat.

CREATE PROCEDURE <NAME>
AS EXTERNAL NAME <ASSEMBLY>.<TYPE>.<METHOD>

Beispiel

CREATE PROCEDURE spDoSomething
AS EXTERNAL NAME [abapguru.SqlServer].SqlIntegration.DoSomething

Dieses kleine Stück T-SQL erstellt eine neue Stored Procedure, allerdings mit Referenz auf das Coding innerhalb unserer eigens erstellten Assembly. Nun können wir mittels EXEC die Stored Procedure aufrufen.

EXEC spDoSomething

Sollte die Stored Procedure parametrisiert sein, können die Parameter (wie gewohnt) in T-SQL spezifizert werden. Achtung! Die Signatur muss hier exakt identisch sein. Beispiel:

CREATE PROCEDURE spDoSomethingWithParams
  @NUMBER INT
AS EXTERNAL NAME [abapguru.SqlServer].SqlIntegration.DoSomethingWithParams

Ähnlich funktionerit das ganze auch mit Funktionen

CREATE FUNCTION CalculateSomething (
  @X INT,
  @Y INT
) RETURNS INT
AS EXTERNAL NAME [abapguru.SqlServer].SqlIntegration.CalculateSomething

Auch hier kann, nach absetzen des obigen Codes die Funktion über SELECT aufgerufen werden.

SELECT CalculateSomething(5, 5)

Fehler 6506 vorbeugen

Solltet Ihr den Fehler mit der Nummer 6506 (Could not find Type in Assembly) beim mappen der Funktionen und SPs bekommen, hilft mit ziemlicher sicherheit folgender Trick. Als erstes sollte sichergestellt werden dass die CLR Integration prinzipiell aktiviert ist. Wenn nicht, bitte mit folgendem Befehl aktivieren.

EXEC sp_configure 'clr enabled', 1
GO
RECONFIGURE

Wenn dieser Schritt erledigt ist und der Fehler euch immer noch nervt hilft folgendes. Aus einem (mir) unerklärlichen Grund muss der Assemblyname im Klassennamen noch einmal auftauchen. Beispiel:

Liefert den Fehler 6506

AS EXTERNAL NAME [abapguru.SqlServer].SqlIntegration.CalculateSomething

Läuft ohne Fehler durch

AS EXTERNAL NAME [abapguru.SqlServer].[abapguru.SqlServer.SqlIntegration].CalculateSomething

Geschafft!

Eine Anmerkung noch am Ende: Bitte extremste Sorgfalt beim Entwickeln von CLR Code welcher in SQL Server integriert wird. Sollte z.B. eine unbehandelte Ausnahme innerhalb des Codings auftreten kann es sein dass der SQL Server die komplette AppDomain entladen muss, was zu enormen Performanceproblemen führen kann.

Bei gelegenheit werde ich noch einen Post zum Thema Performance tippen – mal sehen wie es die Zeit zulässt.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.