Programmieren benutzerdefinierter Typen
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 Codebeispiel in diesem Abschnitt veranschaulicht die Implementierung eines UDT namens Point
als struct
(bzw. Structure
in Visual Basic). Der UDT Point
besteht aus X- und Y-Koordinaten, die als Eigenschaftenprozeduren 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 Ihrer UDT erforderlich sind, und der System.Data.SqlTypes
Namespace enthält die Klassen, die SQL Server nativen 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 überdies den System.Text
-Namespace zum Arbeiten mit Zeichenfolgen.
Hinweis
Visual C++-Datenbankobjekte wie UDTs, die mit /clr:pure
kompiliert wurden, werden nicht für die Ausführung 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.
Die Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute
ist erforderlich. Das Serializable
-Attribut ist optional. Sie können auch das Microsoft.SqlServer.Server.SqlFacetAttribute
-Attribut angeben, um Informationen über den Rückgabetyp eines UDTs 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 den Point
-UDT auf Native
fest. IsByteOrdered
wird auf true
festgelegt. Dies garantiert, dass Vergleiche in SQL Server dieselben Ergebnisse liefern wie Vergleiche in verwaltetem Code. Der UDT implementiert die System.Data.SqlTypes.INullable
-Schnittstelle, damit der UDT NULL erkennt.
Das folgende Codefragment zeigt die Attribute für den 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 NULL-fähig. Damit die UDT jedoch einen NULL-Wert erkennt, muss die UDT die System.Data.SqlTypes.INullable
Schnittstelle implementieren.
Sie müssen eine Eigenschaft namens IsNull
erstellen, mit der im CLR-Code bestimmt werden kann, ob ein Wert NULL ist. Wenn SQL Server einen NULL-instance eines UDT findet, wird der UDT mit normalen NULL-Behandlungsmethoden 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 NULLs wird jedes Mal durchgeführt, wenn ein UDT von der CLR übernommen wird. Dies bedeutet, dass die Verwendung des Transact-SQL IS NULL-Konstrukts zur Überprüfung 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
stellt in keiner Weise einen Sonderfall dar. Wenn eine Point
-Variable @p
gleich Null
ist, dann ergibt @p.IsNull
standardmäßig "NULL", nicht "1". Dies ist so, weil das SqlMethod(OnNullCall)
-Attribut der IsNull get()
-Methode den Standardwert false hat. Weil das Objekt Null
ist, wird es nicht deserialisiert, wenn die Eigenschaft angefordert wird, die Methode wird nicht aufgerufen, und der Standardwert "NULL" wird zurückgegeben.
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. Der UDT muss auch über eine statische Eigenschaft namens Null
verfügen, welche die NULL-Wertinstanz des UDTs 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
Betrachten Sie eine Tabelle, die das Schema Points(id int, location Point), wobei Point
ein CLR UDT ist, und die folgenden Abfragen enthält:
--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 Positionen ungleich Null
zurück. In Abfrage 1 wird die normale NULL-Behandlung verwendet, und dort ist keine Deserialisierung von UDTs erforderlich. In Abfrage 2 dagegen muss jedes Objekt, das nicht Null
ist, deserialisiert und die CLR aufgerufen werden, um den Wert der IsNull
-Eigenschaft zu erhalten. Es ist klar, dass die Verwendung IS NULL
eine bessere Leistung aufweist, und es sollte nie einen Grund geben, die IsNull
Eigenschaft eines UDT aus Transact-SQL-Code zu lesen.
Wozu dient also die IsNull
-Eigenschaft? Erstens wird sie benötigt, damit im CLR-Code bestimmt werden kann, ob ein Wert Null
ist. Zweitens muss der Server prüfen können, ob eine Instanz Null
ist, und hierzu wird diese Eigenschaft vom Server verwendet. Sobald der Server bestimmt hat, dass die Instanz UDT Null
ist, kann er seine systemeigene NULL-Behandlung verwenden.
Implementieren der Parse-Methode
Die Parse
-Methode und die ToString
-Methode ermöglichen die Konvertierung des UDT in Zeichenfolgendarstellungen und umgekehrt von Zeichenfolgen in den UDT. Die Parse
-Methode lässt das Konvertieren einer Zeichenfolge in einen UDT zu. Die Zeichenfolge muss als static
(oder Shared
in Visual Basic) deklariert werden und einen Parameter vom Typ System.Data.SqlTypes.SqlString
verwenden.
Im folgenden Code wird die Parse
-Methode für den Point
-UDT implementiert, welche die X- und Y-Koordinaten einer Instanz einzeln ausgibt. Die Parse
-Methode verfügt über nur ein Argument des Typs System.Data.SqlTypes.SqlString
und setzt voraus, dass die X- und Y-Werte in einer durch Komma begrenzten Zeichenfolge übergeben werden. Durch die Festlegung des Microsoft.SqlServer.Server.SqlMethodAttribute.OnNullCall
-Attributs auf false
wird verhindert, dass die Parse
-Methode für eine 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 den 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 das Ergebnis der Parse
-Methode mithilfe eines System.Text.StringBuilder
um und gibt eine durch Kommas begrenzte System.String
-Instanz zurück, die aus den Koordinatenwerten X und Y besteht. Da InvokeIfReceiverIsNull standardmäßig auf false festgelegt ist, ist die Überprüfung auf eine NULL-instance 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
Der UDT Point
macht die X- und Y-Koordinaten verfügbar, die als öffentliche Eigenschaften des System.Int32
-Typs mit Lese-/Schreibzugriff 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
Bei der Arbeit mit UDT-Daten konvertiert SQL Server Datenbank-Engine binärwerte 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. Damit wird sichergestellt, dass der Wert in das binäre Format zurückkonvertiert 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.
In der Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.ValidationMethodName
-Eigenschaft von 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 einen UDT konvertiert werden. ValidationMethodName
wird auch beim Ausführen des bcp-Hilfsprogramms, bei BULK INSERT, DBCC CHECKDB, DBCC CHECKFILEGROUP, DBCC CHECKTABLE und verteilten Abfragen sowie bei TDS-Vorgängen (Tabular Data Stream) und Remoteprozeduraufrufen (Remote Procedure Call, RPC) aufgerufen. Der Standardwert für ValidationMethodName
lautet NULL, womit angegeben wird, dass keine Validierungsmethode festgelegt ist.
Beispiel
Das folgende Codefragment zeigt die Deklaration für die Point
-Klasse, die mit ValidationMethodName
als Validierungsmethode 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 Validierungsmethode kann einen beliebigen Gültigkeitsbereich haben und sollte true
zurückgeben, wenn der Wert zulässig ist, und andernfalls false
. Wenn die Methode false
zurückgibt oder eine Ausnahme auslöst, wird der Wert als nicht zulässig behandelt, und es wird ein Fehler generiert.
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 Validierungsmethode auf, wenn der Server Konvertierungen durchführt, nicht, wenn Daten durch Festlegen einzelner Eigenschaften eingefügt werden oder wenn Daten mithilfe einer Transact-SQL INSERT-Anweisung eingefügt werden.
Sie müssen die Validierungsmethode explizit von Eigenschaftensettern und der Parse
-Methode aufrufen, wenn die Validierungsmethode 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 von der Parse
-Methode und von den Eigenschaftenprozeduren aufrufen, die die Koordinatenwerte X und Y festlegen. Das folgende Codefragment zeigt, wie die ValidatePoint
Validierungsmethode über die 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 Validierungsmethode aus den ValidatePoint
Eigenschaftenprozeduren 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 die UDT auswirkt. In vielen Fällen können UDTs mithilfe der Alter Assembly-Anweisung von Transact-SQL neu geladen werden. Dies kann jedoch zu Problemen mit vorhandenen Daten führen. Die in der Currency
AdventureWorks-Beispieldatenbank enthaltene UDT verwendet beispielsweise 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. Die Trennung der ConvertCurrency-Funktion von der Currency
UDT-Implementierung bietet mehr 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. Jede dieser Methoden gibt einen Wert vom Typ double
zurück, wobei die Entfernung von Point
zum Nullpunkt, die Entfernung von einem angegebenen Punkt zu Point
und die Entfernung von den angegebenen X- und Y-Koordinaten zu Point
berechnet wird Distanceund DistanceFrom rufen jeweils DistanceFromXY auf, und veranschaulichen, wie verschiedene Argumente für jede Methode verwendet werden.
' 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 zur Verfügung, mit denen Methodendefinitionen markiert werden können, um einen Determinismus anzugeben, das Verhalten beim Aufruf für NULL-Werte festzulegen und anzugeben, ob eine Methode eine Mutatormethode ist. 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, daher erbt die SqlMethodAttribute
-Klasse die Felder FillRowMethodName
und TableDefinition
von SqlFunctionAttribute
. Dies impliziert, dass es möglich ist, eine Tabellenwertmethode zu schreiben. Dies ist jedoch nicht der Fall. Die -Methode wird kompiliert, und die Assembly wird bereitgestellt, aber ein Fehler über den IEnumerable
Rückgabetyp wird zur Laufzeit mit der folgenden Meldung ausgelöst: "Methode, Eigenschaft oder Feldname><" in der Klasse "<class>" in assembly '<assembly>' hat ungültigen Rückgabetyp.
In der folgenden Tabelle werden einige der relevanten Microsoft.SqlServer.Server.SqlMethodAttribute
-Eigenschaften beschrieben, die in UDT-Methoden verwendet werden können, und die zugehörigen Standardwerte aufgeführt.
DataAccess
Gibt an, ob die Funktion Zugriff auf in der lokalen Instanz von SQL Server gespeicherte Benutzerdaten einschließt. Der Standardwert lautet DataAccessKind
None
.
IsDeterministic
Gibt an, ob die Funktion bei denselben Eingabewerten und demselben Datenbankzustand auch immer dieselben Ausgabewerte erzeugt. Der Standardwert ist false
.
IsMutator
Gibt an, ob die Methode eine Statusänderung in der UDT-Instanz verursacht. Der Standardwert ist false
.
IsPrecise
Gibt an, ob die Funktion ungenaue Berechnungen beinhaltet, z. B. Gleitkommaoperationen. Der Standardwert ist 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 Status einer Instanz eines UDTs gestattet. 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 Mutatormethode gekennzeichnete Methode nicht void
zurückgibt (oder kein Sub
in Visual Basic ist), führt CREATE TYPE zu einem Fehler.
Die folgende Anweisung setzt die Existenz des UDTs Triangles
voraus, der über die 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, damit SQL Server die Methode als Mutatormethode markieren kann. Im Code wird zudem OnNullCall
auf false
festgelegt. Dem Server wird damit angegeben, dass die Methode einen NULL-Verweis (Nothing
in Visual Basic) zurückgibt, wenn einer der Eingabeparameter einen NULL-Verweis enthält.
<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
Zur Implementierung eines UDTs mit einem benutzerdefinierten Format müssen Sie die Read
-Methode und die Write
-Methode implementieren. Diese Methoden implementieren die Microsoft.SqlServer.Server.IBinarySerialize-Schnittstelle zur Serialisierung und Deserialisierung der UDT-Daten. Sie müssen zudem die MaxByteSize
-Eigenschaft des Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute
-Attributs angeben.
Der UDT Currency
Die Currency
UDT ist in den CLR-Beispielen enthalten, die ab SQL Server 2005 mit SQL Server installiert werden können.
Der UDT Currency
kann Währungsbeträge im Währungssystem eines bestimmten Lands verarbeiten. Sie müssen zwei Felder definieren: ein string
-Feld für CultureInfo
, das angibt, welches Land die Währung ausgegeben hat (beispielsweise en-us) und ein decimal
-Feld für CurrencyValue
, den Währungsbetrag.
Obwohl sie vom Server nicht zum Durchführen von Vergleichen verwendet wird, implementiert der UDT Currency
die System.IComparable
-Schnittstelle, die nur eine Methode namens System.IComparable.CompareTo
bereitstellt. 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. Für Transact-SQL-Code bestimmen die folgenden Aktionen den Vergleich:
Legen Sie das
IsByteOrdered
Attribut auf true fest, was SQL Server angibt, die persistente binäre Darstellung auf dem Datenträger für Vergleiche zu verwenden.Verwenden Sie die
Write
UdT-Methode,Currency
um zu bestimmen, wie die UDT auf dem Datenträger beibehalten wird und wie UDT-Werte für Transact-SQL-Vorgänge verglichen und sortiert werden.Speichern Sie den UDT
Currency
im folgenden binären Format: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.
Verwenden Sie Bytes 20 und nachfolgende Bytes zum Speichern des Dezimalwerts der Währungsbetrags.
Der Zweck des Auffüllungsbereichs besteht darin, sicherzustellen, dass die Kultur vollständig vom Währungswert getrennt ist. Wenn ein UDT im Transact-SQL-Code mit einem anderen verglichen wird, werden Kulturbytes mit Kulturbytes verglichen und Währungsbytewerte mit Währungsbytewerten verglichen.
Befolgen Sie für die vollständige Codeauflistung für die Currency
UDT die Anweisungen zum Installieren der CLR-Beispiele in SQL Server Beispiele für Datenbank-Engine.
Currency-Attribute
Der UDT Currency
wurde 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
wählen, müssen Sie auch die IBinarySerialize
-Schnittstelle implementieren und eigene Read
- und Write
-Methoden schreiben. In den folgenden Prozeduren aus dem UDT Currency
werden System.IO.BinaryReader
und System.IO.BinaryWriter
zum Lesen bzw. zum Schreiben von UDT-Werten verwendet.
' 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();
}
Die vollständige Codeauflistung für die Currency
UDT finden Sie unter beispiele für SQL Server-Datenbank-Engine.