Teilen über


Erstellen benutzerdefinierter Typen: Codieren

Gilt für: SQL Server

Wenn Sie die Definition eines benutzerdefinierten Typs (UDT) schreiben, müssen Sie verschiedene Funktionen implementieren, abhängig davon, ob Sie den UDT als Klasse oder als Struktur implementieren, sowie abhängig von den von Ihnen gewählten Format- und Serialisierungsoptionen.

Das Beispiel in diesem Abschnitt veranschaulicht die Implementierung eines Point UDT als Struktur (oder Struktur in Visual Basic). Das Point UDT besteht aus X- und Y-Koordinaten, die als Eigenschaftsprozeduren implementiert werden.

Beim Definieren eines UDTs sind die folgenden Namespaces erforderlich:

Imports System  
Imports System.Data.SqlTypes  
Imports Microsoft.SqlServer.Server  
using System;  
using System.Data.SqlTypes;  
using Microsoft.SqlServer.Server;  

Der Microsoft.SqlServer.Server-Namespace enthält die Objekte, die für verschiedene Attribute Ihres UDT erforderlich sind, und der System.Data.SqlTypes-Namespace enthält die Klassen, die sql Server native Datentypen darstellen, die für die Assembly verfügbar sind. Es kann natürlich zusätzliche Namespaces geben, die eine Assembly erfordert, um ordnungsgemäß zu funktionieren. Der Point UDT verwendet auch den System.Text-Namespace zum Arbeiten mit Zeichenfolgen.

Hinweis

Visual C++-Datenbankobjekte, z. B. UDTs, kompiliert mit /clr:pure werden für die Ausführung nicht unterstützt.

Angeben von Attributen

Attribute bestimmen, wie die Serialisierung verwendet wird, um die Speicherdarstellung von UDTs zu erstellen und um UDTs durch Werte an den Client zu übertragen.

Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute ist erforderlich. Das Serializable-Attribut ist optional. Sie können auch microsoft.SqlServer.Server.SqlFacetAttribute angeben, um Informationen zum Rückgabetyp eines UDT bereitzustellen. Weitere Informationen finden Sie unter Benutzerdefinierte Attribute für CLR-Routinen.

Attribute des Point-UDT

Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute legt das Speicherformat für das Point UDT auf Native fest. IsByteOrdered ist auf "true" festgelegt, wodurch sichergestellt wird, dass die Ergebnisse von Vergleichen in SQL Server identisch sind, als ob derselbe Vergleich im verwalteten Code stattgefunden hätte. Das UDT implementiert die System.Data.SqlTypes.INullable-Schnittstelle , um udT null zu erkennen.

Das folgende Codefragment zeigt die Attribute für point UDT.

<Serializable(), SqlUserDefinedTypeAttribute(Format.Native, _  
  IsByteOrdered:=True)> _  
  Public Structure Point  
    Implements INullable  
[Serializable]  
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native,  
  IsByteOrdered=true)]  
public struct Point : INullable  
{  

Implementieren von NULL-Zulässigkeit

Zusätzlich zum ordnungsgemäßen Angeben der Attribute für die Assemblys muss der UDT auch die NULL-Zulässigkeit unterstützen. UDTs, die in SQL Server geladen werden, sind nullfähig. Damit udT jedoch einen Nullwert erkennt, muss udT die System.Data.SqlTypes.INullable-Schnittstelle implementieren.

Sie müssen eine Eigenschaft mit dem Namen IsNull erstellen, die erforderlich ist, um zu bestimmen, ob ein Wert null aus dem CLR-Code ist. Wenn SQL Server eine NULL-Instanz eines UDT findet, wird der UDT mithilfe normaler Nullbehandlungsmethoden beibehalten. Der Server vergeudet keine Zeit mit dem Serialisieren oder Deserialisieren des UDTs, wenn dies nicht erforderlich ist, und er verschwendet keinen Platz zum Speichern des NULL-UDTs. Diese Überprüfung auf NULL-Werte wird jedes Mal ausgeführt, wenn ein UDT von der CLR übernommen wird, was bedeutet, dass die Verwendung des Transact-SQL IS NULL-Konstrukts zum Überprüfen auf NULL-UDTs immer funktionieren sollte. Die IsNull-Eigenschaft wird auch vom Server verwendet, um zu testen, ob eine Instanz null ist. Sobald der Server bestimmt, dass der UDT NULL ist, kann er seine systemeigene NULL-Behandlung verwenden.

Die get() -Methode von IsNull ist in keiner Weise mit Sonderfall gekennzeichnet. Wenn eine Punktvariable @p Null ist, wird standardmäßig @p.IsNull als "NULL" ausgewertet, nicht "1". Dies liegt daran, dass das SqlMethod(OnNullCall) -Attribut der IsNull get() -Methode standardmäßig auf "false" festgelegt ist. Da das Objekt Null ist, wird die Methode nicht aufgerufen, und ein Standardwert von "NULL" wird zurückgegeben, wenn die Eigenschaft angefordert wird, das Objekt nicht deserialisiert wird.

Beispiel

Im folgenden Beispiel ist die is_Null-Variable privat und enthält für die Instanz des UDT den Status NULL. Im Code muss ein entsprechender Wert für is_Null verwaltet werden. Das UDT muss auch über eine statische Eigenschaft mit dem Namen Null verfügen, die eine NULL-Wertinstanz des UDT zurückgibt. Dadurch kann der UDT einen NULL-Wert zurückgeben, wenn die Instanz auch in der Datenbank tatsächlich NULL ist.

Private is_Null As Boolean  
  
Public ReadOnly Property IsNull() As Boolean _  
   Implements INullable.IsNull  
    Get  
        Return (is_Null)  
    End Get  
End Property  
  
Public Shared ReadOnly Property Null() As Point  
    Get  
        Dim pt As New Point  
        pt.is_Null = True  
        Return (pt)  
    End Get  
End Property  
private bool is_Null;  
  
public bool IsNull  
{  
    get  
    {  
        return (is_Null);  
    }  
}  
  
public static Point Null  
{  
    get  
    {  
        Point pt = new Point();  
        pt.is_Null = true;  
        return pt;  
    }  
}  

IS NULL und IsNull

Erwägen Sie eine Tabelle, die das Schema Points(id int, location Point) enthält, wobei Point ein CLR-UDT ist, und die folgenden Abfragen:

--Query 1  
SELECT ID  
FROM Points  
WHERE NOT (location IS NULL) -- Or, WHERE location IS NOT NULL;  
--Query 2:  
SELECT ID  
FROM Points  
WHERE location.IsNull = 0;  

Beide Abfragen geben die IDs von Punkten mit Nicht-Null-Speicherorten zurück. In Abfrage 1 wird die normale NULL-Behandlung verwendet, und dort ist keine Deserialisierung von UDTs erforderlich. Abfrage 2 muss dagegen jedes Nicht-Null-Objekt deserialisieren und den CLR aufrufen, um den Wert der IsNull-Eigenschaft abzurufen. Eindeutig zeigt die Verwendung von IS NULL eine bessere Leistung, und es sollte niemals ein Grund zum Lesen der IsNull-Eigenschaft eines UDT aus Transact-SQL-Code geben.

Was ist also die Verwendung der IsNull-Eigenschaft ? Zunächst ist es erforderlich, um zu bestimmen, ob ein Wert null aus dem CLR-Code ist. Zweitens benötigt der Server eine Möglichkeit, zu testen, ob eine Instanz Null ist, sodass diese Eigenschaft vom Server verwendet wird. Nachdem es festgestellt hat, dass es Null ist, kann er seine systemeigene Nullbehandlung verwenden, um sie zu behandeln.

Implementieren der Parse-Methode

Die Parse - und ToString-Methoden ermöglichen Konvertierungen in und aus Zeichenfolgendarstellungen des UDT. Mit der Parse-Methode kann eine Zeichenfolge in ein UDT konvertiert werden. Sie muss als statisch (oder in Visual Basic freigegeben ) deklariert werden und einen Parameter vom Typ System.Data.SqlTypes.SqlString verwenden.

Der folgende Code implementiert die Parse-Methode für das Point UDT, wodurch die X- und Y-Koordinaten getrennt werden. Die Parse-Methode weist ein einzelnes Argument vom Typ System.Data.SqlTypes.SqlString auf und geht davon aus, dass X- und Y-Werte als durch Trennzeichen getrennte Zeichenfolge angegeben werden. Wenn Sie das Attribut "Microsoft.SqlServer.Server.SqlMethodAttribute.OnNullCall" auf "false " festlegen, wird verhindert, dass die Parse-Methode von einer NULL-Instanz von Point aufgerufen wird.

<SqlMethod(OnNullCall:=False)> _  
Public Shared Function Parse(ByVal s As SqlString) As Point  
    If s.IsNull Then  
        Return Null  
    End If  
  
    ' Parse input string here to separate out points.  
    Dim pt As New Point()  
    Dim xy() As String = s.Value.Split(",".ToCharArray())  
    pt.X = Int32.Parse(xy(0))  
    pt.Y = Int32.Parse(xy(1))  
    Return pt  
End Function  
[SqlMethod(OnNullCall = false)]  
public static Point Parse(SqlString s)  
{  
    if (s.IsNull)  
        return Null;  
  
    // Parse input string to separate out points.  
    Point pt = new Point();  
    string[] xy = s.Value.Split(",".ToCharArray());  
    pt.X = Int32.Parse(xy[0]);  
    pt.Y = Int32.Parse(xy[1]);  
    return pt;  
}  

Implementieren der ToString-Methode

Die ToString-Methode konvertiert das Point UDT in einen Zeichenfolgenwert. In diesem Fall wird die Zeichenfolge "NULL" für eine Null-Instanz des Point-Typs zurückgegeben. Die ToString-Methode kehrt die Parse-Methode um, indem ein System.Text.StringBuilder verwendet wird, um ein durch Trennzeichen getrenntes System.String zurückzugeben, das aus den X- und Y-Koordinatenwerten besteht. Da InvokeIfReceiverIsNull standardmäßig auf "false" festgelegt ist, ist die Überprüfung auf eine NULL-Instanz von Point nicht erforderlich.

Private _x As Int32  
Private _y As Int32  
  
Public Overrides Function ToString() As String  
    If Me.IsNull Then  
        Return "NULL"  
    Else  
        Dim builder As StringBuilder = New StringBuilder  
        builder.Append(_x)  
        builder.Append(",")  
        builder.Append(_y)  
        Return builder.ToString  
    End If  
End Function  
private Int32 _x;  
private Int32 _y;  
  
public override string ToString()  
{  
    if (this.IsNull)  
        return "NULL";  
    else  
    {  
        StringBuilder builder = new StringBuilder();  
        builder.Append(_x);  
        builder.Append(",");  
        builder.Append(_y);  
        return builder.ToString();  
    }  
}  

Bereitstellen der UDT-Eigenschaften

Das Point UDT macht X- und Y-Koordinaten verfügbar, die als öffentliche Lese-/Schreibeigenschaften vom Typ System.Int32 implementiert werden.

Public Property X() As Int32  
    Get  
        Return (Me._x)  
    End Get  
  
    Set(ByVal Value As Int32)  
        _x = Value  
    End Set  
End Property  
  
Public Property Y() As Int32  
    Get  
        Return (Me._y)  
    End Get  
  
    Set(ByVal Value As Int32)  
        _y = Value  
    End Set  
End Property  
public Int32 X  
{  
    get  
    {  
        return this._x;  
    }  
    set   
    {  
        _x = value;  
    }  
}  
  
public Int32 Y  
{  
    get  
    {  
        return this._y;  
    }  
    set  
    {  
        _y = value;  
    }  
}  

Überprüfen von UDT-Werten

Beim Arbeiten mit UDT-Daten konvertiert SQL Server Datenbank-Engine binäre Werte automatisch in UDT-Werte. Im Rahmen dieses Konvertierungsprozesses muss überprüft werden, ob sich die Werte für das Serialisierungsformat des Typs eignen, und sichergestellt werden, dass die Werte ordnungsgemäß deserialisiert werden können. Dadurch wird sichergestellt, dass der Wert wieder in binäre Form konvertiert werden kann. Bei UDTs, deren Sortierreihenfolge eine Bytereihenfolge ist, wird dadurch auch sichergestellt, dass der resultierende Binärwert dem ursprünglichen Binärwert entspricht. So wird verhindert, dass ungültige Werte in der Datenbank persistent gespeichert werden. In einigen Fällen ist diese Art der Überprüfung möglicherweise unzulänglich. Eine zusätzliche Überprüfung kann erforderlich sein, wenn UDT-Werte in einer bestimmten Domäne oder einem Bereich liegen müssen. Ein UDT beispielsweise, der ein Datum implementiert, kann erfordern, dass der Wert für den Tag eine positive Zahl ist, die in einem bestimmten zulässigen Wertebereich liegt.

Mit der Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.ValidationMethodName-Eigenschaft der Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute können Sie den Namen einer Überprüfungsmethode angeben, die der Server ausführt, wenn Daten einem UDT zugewiesen oder in ein UDT konvertiert werden. ValidationMethodName wird auch während der Ausführung des bcp-Dienstprogramms, BULK INSERT, DBCC CHECKDB, DBCC CHECKFILEGROUP, DBCC CHECKTABLE, distributed query, and tabular data stream (TDS) remote procedure call (RPC) operations aufgerufen. Der Standardwert für ValidationMethodName ist null, was angibt, dass keine Überprüfungsmethode vorhanden ist.

Beispiel

Das folgende Codefragment zeigt die Deklaration für die Point-Klasse , die einen ValidationMethodName von ValidatePoint angibt.

<Serializable(), SqlUserDefinedTypeAttribute(Format.Native, _  
  IsByteOrdered:=True, _  
  ValidationMethodName:="ValidatePoint")> _  
  Public Structure Point  
[Serializable]  
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native,  
  IsByteOrdered=true,   
  ValidationMethodName = "ValidatePoint")]  
public struct Point : INullable  
{  

Wenn eine Validierungsmethode angegeben wird, muss diese eine Signatur aufweisen, die etwa folgendem Codefragment entspricht:

Private Function ValidationFunction() As Boolean  
    If (validation logic here) Then  
        Return True  
    Else  
        Return False  
    End If  
End Function  
private bool ValidationFunction()  
{  
    if (validation logic here)  
    {  
        return true;  
    }  
    else  
    {  
        return false;  
    }  
}  

Die Überprüfungsmethode kann einen beliebigen Bereich aufweisen und sollte "true" zurückgeben, wenn der Wert gültig ist, andernfalls "false". Wenn die Methode "false" zurückgibt oder eine Ausnahme auslöst, wird der Wert als ungültig behandelt, und ein Fehler wird ausgelöst.

Im nachstehenden Beispiel lässt der Code für die X- und Y-Koordinaten nur Werte zu, die größer oder gleich Null sind.

Private Function ValidatePoint() As Boolean  
    If (_x >= 0) And (_y >= 0) Then  
        Return True  
    Else  
        Return False  
    End If  
End Function  
private bool ValidatePoint()  
{  
    if ((_x >= 0) && (_y >= 0))  
    {  
        return true;  
    }  
    else  
    {  
        return false;  
    }  
}  

Einschränkungen von Validierungsmethoden

Der Server ruft die Überprüfungsmethode auf, wenn der Server Konvertierungen ausführt, nicht beim Einfügen von Daten durch Festlegen einzelner Eigenschaften oder beim Einfügen von Daten mithilfe einer Transact-SQL INSERT-Anweisung.

Sie müssen die Überprüfungsmethode explizit von Eigenschaftensatzern und der Parse-Methode aufrufen, wenn die Überprüfungsmethode in allen Situationen ausgeführt werden soll. Dies ist keine Voraussetzung, und in einigen Fällen ist es möglicherweise nicht einmal wünschenswert.

Beispiel für die Validierung in der Parse-Methode

Um sicherzustellen, dass die ValidatePoint-Methode in der Point-Klasse aufgerufen wird, müssen Sie sie aus der Parse-Methode und aus den Eigenschaftenprozeduren aufrufen, die die X- und Y-Koordinatenwerte festlegen. Das folgende Codefragment zeigt, wie die ValidatePoint-Validierungsmethode aus der Parse-Funktion aufgerufen wird.

<SqlMethod(OnNullCall:=False)> _  
Public Shared Function Parse(ByVal s As SqlString) As Point  
    If s.IsNull Then  
        Return Null  
    End If  
  
    ' Parse input string here to separate out points.  
    Dim pt As New Point()  
    Dim xy() As String = s.Value.Split(",".ToCharArray())  
    pt.X = Int32.Parse(xy(0))  
    pt.Y = Int32.Parse(xy(1))  
  
    ' Call ValidatePoint to enforce validation  
    ' for string conversions.  
    If Not pt.ValidatePoint() Then  
        Throw New ArgumentException("Invalid XY coordinate values.")  
    End If  
    Return pt  
End Function  
[SqlMethod(OnNullCall = false)]  
public static Point Parse(SqlString s)  
{  
    if (s.IsNull)  
        return Null;  
  
    // Parse input string to separate out points.  
    Point pt = new Point();  
    string[] xy = s.Value.Split(",".ToCharArray());  
    pt.X = Int32.Parse(xy[0]);  
    pt.Y = Int32.Parse(xy[1]);  
  
    // Call ValidatePoint to enforce validation  
    // for string conversions.  
    if (!pt.ValidatePoint())   
        throw new ArgumentException("Invalid XY coordinate values.");  
    return pt;  
}  

Beispiel für die Validierung von Eigenschaften

Das folgende Codefragment zeigt, wie die ValidatePoint-Validierungsmethode aus den Eigenschaftsprozeduren aufgerufen wird, die die X- und Y-Koordinaten festlegen.

Public Property X() As Int32  
    Get  
        Return (Me._x)  
    End Get  
  
    Set(ByVal Value As Int32)  
        Dim temp As Int32 = _x  
        _x = Value  
        If Not ValidatePoint() Then  
            _x = temp  
            Throw New ArgumentException("Invalid X coordinate value.")  
        End If  
    End Set  
End Property  
  
Public Property Y() As Int32  
    Get  
        Return (Me._y)  
    End Get  
  
    Set(ByVal Value As Int32)  
        Dim temp As Int32 = _y  
        _y = Value  
        If Not ValidatePoint() Then  
            _y = temp  
            Throw New ArgumentException("Invalid Y coordinate value.")  
        End If  
    End Set  
End Property  
    public Int32 X  
{  
    get  
    {  
        return this._x;  
    }  
    // Call ValidatePoint to ensure valid range of Point values.  
    set   
    {  
        Int32 temp = _x;  
        _x = value;  
        if (!ValidatePoint())  
        {  
            _x = temp;  
            throw new ArgumentException("Invalid X coordinate value.");  
        }  
    }  
}  
  
public Int32 Y  
{  
    get  
    {  
        return this._y;  
    }  
    set  
    {  
        Int32 temp = _y;  
        _y = value;  
        if (!ValidatePoint())  
        {  
            _y = temp;  
            throw new ArgumentException("Invalid Y coordinate value.");  
        }  
    }  
}  

Codieren von UDT-Methoden

Bei Codieren von UDT-Methoden müssen Sie berücksichtigen, ob sich der verwendete Algorithmus möglicherweise im Lauf der Zeit ändern könnte. Wenn dem so ist, sollten Sie erwägen, eine separate Klasse für die vom UDT verwendeten Methoden zu erstellen. Wenn sich der Algorithmus ändert, können Sie die Klasse mit dem neuen Code neu kompilieren und die Assembly in SQL Server laden, ohne dass sich dies auf das UDT auswirkt. In vielen Fällen können UDTs mithilfe der Transact-SQL ALTER ASSEMBLY-Anweisung neu geladen werden, dies kann jedoch möglicherweise zu Problemen mit vorhandenen Daten führen. Beispielsweise verwendet die in der AdventureWorks-Beispieldatenbank enthaltene Währungs-UDT eine ConvertCurrency-Funktion zum Konvertieren von Währungswerten, die in einer separaten Klasse implementiert wird. Es ist möglich, dass sich die Konvertierungsalgorithmen in der Zukunft auf unvorhersehbare Weise ändern oder dass eine neue Funktionalität erforderlich wird. Das Trennen der ConvertCurrency-Funktion von der UdT-Implementierung "Währung" bietet größere Flexibilität bei der Planung zukünftiger Änderungen.

Beispiel

Die Point-Klasse enthält drei einfache Methoden zum Berechnen der Entfernung: Distance, DistanceFrom und DistanceFromXY. Jeder gibt einen doppelten Berechnen des Abstands von Punkt zu Null, den Abstand von einem angegebenen Punkt zu Punkt und den Abstand von angegebenen X- und Y-Koordinaten zu Punkt zurück. Distance and DistanceFrom each call DistanceFromXY, and demonstrate how to use different arguments for each method.

' Distance from 0 to Point.  
<SqlMethod(OnNullCall:=False)> _  
Public Function Distance() As Double  
    Return DistanceFromXY(0, 0)  
End Function  
  
' Distance from Point to the specified point.  
<SqlMethod(OnNullCall:=False)> _  
Public Function DistanceFrom(ByVal pFrom As Point) As Double  
    Return DistanceFromXY(pFrom.X, pFrom.Y)  
End Function  
  
' Distance from Point to the specified x and y values.  
<SqlMethod(OnNullCall:=False)> _  
Public Function DistanceFromXY(ByVal ix As Int32, ByVal iy As Int32) _  
    As Double  
    Return Math.Sqrt(Math.Pow(ix - _x, 2.0) + Math.Pow(iy - _y, 2.0))  
End Function  
// Distance from 0 to Point.  
[SqlMethod(OnNullCall = false)]  
public Double Distance()  
{  
    return DistanceFromXY(0, 0);  
}  
  
// Distance from Point to the specified point.  
[SqlMethod(OnNullCall = false)]  
public Double DistanceFrom(Point pFrom)  
{  
    return DistanceFromXY(pFrom.X, pFrom.Y);  
}  
  
// Distance from Point to the specified x and y values.  
[SqlMethod(OnNullCall = false)]  
public Double DistanceFromXY(Int32 iX, Int32 iY)  
{  
    return Math.Sqrt(Math.Pow(iX - _x, 2.0) + Math.Pow(iY - _y, 2.0));  
}  

Verwenden von SqlMethod-Attributen

Die Microsoft.SqlServer.Server.SqlMethodAttribute-Klasse stellt benutzerdefinierte Attribute bereit, mit denen Methodendefinitionen markiert werden können, um den Determinismus, das Verhalten bei Nullaufrufen anzugeben und anzugeben, ob es sich bei einer Methode um einen Mutator handelt. Bei diesen Eigenschaften werden die Standardwerte vorausgesetzt, und das benutzerdefinierte Attribut wird nur verwendet, wenn ein anderer Wert als der Standardwert erforderlich ist.

Hinweis

Die SqlMethodAttribute-Klasse erbt von der SqlFunctionAttribute-Klasse, sodass SqlMethodAttribute die Felder FillRowMethodName und TableDefinition von SqlFunctionAttribute erbt. Dies impliziert, dass es möglich ist, eine Tabellenwertmethode zu schreiben. Dies ist jedoch nicht der Fall. Die Methode kompiliert und die Assembly wird bereitgestellt, aber zur Laufzeit wird ein Fehler des Rückgabetyps "IEnumerable " ausgelöst, wobei die folgende Meldung angezeigt wird: "Methode, Eigenschaft oder Feld '<Name>' in der Klasse '<Klasse>' in assembly '<assembly>' hat ungültigen Rückgabetyp."

Die folgende Tabelle beschreibt einige der relevanten Microsoft.SqlServer.Server.SqlMethodAttribute-Eigenschaften , die in UDT-Methoden verwendet werden können, und listet ihre Standardwerte auf.

DataAccess
Gibt an, ob die Funktion Zugriff auf in der lokalen Instanz von SQL Server gespeicherte Benutzerdaten einschließt. Der Standardwert ist DataAccessKind.Keine.

IsDeterministic
Gibt an, ob die Funktion bei denselben Eingabewerten und demselben Datenbankzustand auch immer dieselben Ausgabewerte erzeugt. Die Standardeinstellung lautet false.

IsMutator
Gibt an, ob die Methode eine Statusänderung in der UDT-Instanz verursacht. Die Standardeinstellung lautet false.

IsPrecise
Gibt an, ob die Funktion ungenaue Berechnungen beinhaltet, z. B. Gleitkommaoperationen. Die Standardeinstellung lautet false.

OnNullCall
Gibt an, ob die Methode aufgerufen wird, wenn als Eingabeargumente NULL-Verweise angegeben werden. Der Standardwert ist true.

Beispiel

Mit der Microsoft.SqlServer.Server.SqlMethodAttribute.IsMutator-Eigenschaft können Sie eine Methode markieren, die eine Änderung des Zustands einer Instanz eines UDT zulässt. Transact-SQL ermöglicht es Ihnen nicht, zwei UDT-Eigenschaften in der SET-Klausel einer UPDATE-Anweisung festzulegen. Sie können jedoch eine Methode als Mutator markieren, die zwei Elemente ändert.

Hinweis

In Abfragen sind Mutatormethoden nicht zulässig. Sie können nur in Zuweisungsanweisungen oder Datenänderungsanweisungen aufgerufen werden. Wenn eine als Mutator gekennzeichnete Methode keine Leere zurückgibt (oder kein Sub in Visual Basic ist), schlägt CREATE TYPE mit einem Fehler fehl.

Die folgende Anweisung geht davon aus, dass ein Dreiecks-UDT vorhanden ist, das über eine Rotate-Methode verfügt. Die folgende Transact-SQL-Update-Anweisung ruft die Rotate-Methode auf:

UPDATE Triangles SET t.RotateY(0.6) WHERE id=5  

Die Rotate-Methode ist mit der SqlMethod-Attributeinstellung IsMutator auf "true" versehen, sodass SQL Server die Methode als Mutatormethode markieren kann. Der Code legt "OnNullCall " auch auf "false" fest, was für den Server angibt, dass die Methode einen Nullverweis (Nothing in Visual Basic) zurückgibt, wenn einer der Eingabeparameter NULL-Verweise ist.

<SqlMethod(IsMutator:=True, OnNullCall:=False)> _  
Public Sub Rotate(ByVal anglex as Double, _  
  ByVal angley as Double, ByVal anglez As Double)   
   RotateX(anglex)  
   RotateY(angley)  
   RotateZ(anglez)  
End Sub  
[SqlMethod(IsMutator = true, OnNullCall = false)]  
public void Rotate(double anglex, double angley, double anglez)   
{  
   RotateX(anglex);  
   RotateY(angley);  
   RotateZ(anglez);  
}  

Implementieren eines UDTs mit einem benutzerdefinierten Format

Wenn Sie ein UDT mit einem benutzerdefinierten Format implementieren, müssen Sie Lese- und Schreibmethoden implementieren, die die Microsoft.SqlServer.Server.IBinarySerialize-Schnittstelle implementieren, um UDT-Daten serialisieren und deserialisieren zu können. Sie müssen auch die MaxByteSize-Eigenschaft des Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute angeben.

Der UDT Currency

Das Währungs-UDT ist in den CLR-Beispielen enthalten, die mit SQL Server installiert werden können, beginnend mit SQL Server 2005 (9.x).

Die Währungs-UDT unterstützt die Abwicklung von Geldbeträgen im Währungssystem einer bestimmten Kultur. Sie müssen zwei Felder definieren: eine Zeichenfolge für CultureInfo, die angibt, wer die Währung (z. B. "en-us") und ein Dezimalzeichen für CurrencyValue, den Betrag, ausgegeben hat.

Obwohl sie vom Server nicht zum Ausführen von Vergleichen verwendet wird, implementiert das Currency UDT die System.IComparable-Schnittstelle , die eine einzelne Methode verfügbar macht, System.IComparable.CompareTo. Diese Methode wird auf Clientseite in Situationen verwendet, in denen Currency-Werte genau verglichen oder geordnet werden sollen.

Im Code, der in der CLR ausgeführt wird, wird die Länderangabe getrennt vom Währungswert verglichen. Bei Transact-SQL-Code bestimmen die folgenden Aktionen den Vergleich:

  1. Legen Sie das IsByteOrdered-Attribut auf "true" fest, das SQL Server angibt, die permanente binäre Darstellung auf dem Datenträger für Vergleiche zu verwenden.

  2. Verwenden Sie die Write-Methode für die Währungs-UDT , um zu bestimmen, wie das UDT auf dem Datenträger beibehalten wird und wie UDT-Werte für Transact-SQL-Vorgänge verglichen und sortiert werden.

  3. Speichern Sie die Währungs-UDT mit dem folgenden Binärformat:

    1. Speichern Sie die Länderangabe als UTF-16-codierte Zeichenfolge für die Bytes 0 bis 19, wobei die Zeichenfolge rechts mit NULL-Zeichen aufgefüllt werden soll.

    2. Verwenden Sie Bytes 20 und nachfolgende Bytes zum Speichern des Dezimalwerts der Währungsbetrags.

Der Zweck des Abstands besteht darin, sicherzustellen, dass die Kultur vollständig vom Währungswert getrennt ist. Wenn ein UDT mit einem anderen im Transact-SQL-Code verglichen wird, werden Kulturbytes mit Kulturbytes verglichen, und Währungsbytewerte werden mit Währungsbytewerten verglichen.

Currency-Attribute

Das Währungs-UDT wird mit den folgenden Attributen definiert.

<Serializable(), Microsoft.SqlServer.Server.SqlUserDefinedType( _  
    Microsoft.SqlServer.Server.Format.UserDefined, _  
    IsByteOrdered:=True, MaxByteSize:=32), _  
    CLSCompliant(False)> _  
Public Structure Currency  
Implements INullable, IComparable, _  
Microsoft.SqlServer.Server.IBinarySerialize  
[Serializable]  
[SqlUserDefinedType(Format.UserDefined,   
    IsByteOrdered = true, MaxByteSize = 32)]  
    [CLSCompliant(false)]  
    public struct Currency : INullable, IComparable, IBinarySerialize  
    {  

Erstellen von Read- und Write-Methoden mit IBinarySerialize

Wenn Sie das Serialisierungsformat "UserDefined" auswählen, müssen Sie auch die IBinarySerialize-Schnittstelle implementieren und eigene Lese- und Schreibmethoden erstellen. Die folgenden Verfahren aus dem Währungs-UDT verwenden system.IO.BinaryReader und System.IO.BinaryWriter zum Lesen und Schreiben in udT.

' IBinarySerialize methods  
' The binary layout is as follow:  
'    Bytes 0 - 19: Culture name, padded to the right with null  
'    characters, UTF-16 encoded  
'    Bytes 20+: Decimal value of money  
' If the culture name is empty, the currency is null.  
Public Sub Write(ByVal w As System.IO.BinaryWriter) _  
  Implements Microsoft.SqlServer.Server.IBinarySerialize.Write  
    If Me.IsNull Then  
        w.Write(nullMarker)  
        w.Write(System.Convert.ToDecimal(0))  
        Return  
    End If  
  
    If cultureName.Length > cultureNameMaxSize Then  
        Throw New ApplicationException(String.Format(CultureInfo.CurrentUICulture, _  
           "{0} is an invalid culture name for currency as it is too long.", cultureNameMaxSize))  
    End If  
  
    Dim paddedName As String = cultureName.PadRight(cultureNameMaxSize, CChar(vbNullChar))  
  
    For i As Integer = 0 To cultureNameMaxSize - 1  
        w.Write(paddedName(i))  
    Next i  
  
    ' Normalize decimal value to two places  
    currencyVal = Decimal.Floor(currencyVal * 100) / 100  
    w.Write(currencyVal)  
End Sub  
  
Public Sub Read(ByVal r As System.IO.BinaryReader) _  
  Implements Microsoft.SqlServer.Server.IBinarySerialize.Read  
    Dim name As Char() = r.ReadChars(cultureNameMaxSize)  
    Dim stringEnd As Integer = Array.IndexOf(name, CChar(vbNullChar))  
  
    If stringEnd = 0 Then  
        cultureName = Nothing  
        Return  
    End If  
  
    cultureName = New String(name, 0, stringEnd)  
    currencyVal = r.ReadDecimal()  
End Sub  
  
// IBinarySerialize methods  
// The binary layout is as follow:  
//    Bytes 0 - 19:Culture name, padded to the right   
//    with null characters, UTF-16 encoded  
//    Bytes 20+:Decimal value of money  
// If the culture name is empty, the currency is null.  
public void Write(System.IO.BinaryWriter w)  
{  
    if (this.IsNull)  
    {  
        w.Write(nullMarker);  
        w.Write((decimal)0);  
        return;  
    }  
  
    if (cultureName.Length > cultureNameMaxSize)  
    {  
        throw new ApplicationException(string.Format(  
            CultureInfo.InvariantCulture,   
            "{0} is an invalid culture name for currency as it is too long.",   
            cultureNameMaxSize));  
    }  
  
    String paddedName = cultureName.PadRight(cultureNameMaxSize, '\0');  
    for (int i = 0; i < cultureNameMaxSize; i++)  
    {  
        w.Write(paddedName[i]);  
    }  
  
    // Normalize decimal value to two places  
    currencyValue = Decimal.Floor(currencyValue * 100) / 100;  
    w.Write(currencyValue);  
}  
public void Read(System.IO.BinaryReader r)  
{  
    char[] name = r.ReadChars(cultureNameMaxSize);  
    int stringEnd = Array.IndexOf(name, '\0');  
  
    if (stringEnd == 0)  
    {  
        cultureName = null;  
        return;  
    }  
  
    cultureName = new String(name, 0, stringEnd);  
    currencyValue = r.ReadDecimal();  
}  

Weitere Informationen

Erstellen eines benutzerdefinierten Typs