Crear tipos definidos por el usuario: codificación
Se aplica a: SQL Server
Al codificar la definición de un tipo definido por el usuario (UDT), debe implementar varias características, en función de si implementa el UDT como una clase o como una estructura, así como de las opciones de formato y serialización que haya elegido.
En el ejemplo de esta sección se muestra la implementación de un UDT de punto como estructura (o Estructura en Visual Basic). El UDT point consta de coordenadas X e Y implementadas como procedimientos de propiedad.
Para definir un UDT, se requieren los espacios de nombres siguientes:
Imports System
Imports System.Data.SqlTypes
Imports Microsoft.SqlServer.Server
using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
El espacio de nombres Microsoft.SqlServer.Server contiene los objetos necesarios para varios atributos del UDT y el espacio de nombres System.Data.SqlTypes contiene las clases que representan los tipos de datos nativos de SQL Server disponibles para el ensamblado. Es obvio que puede haber espacios de nombres adicionales que el ensamblado necesite para funcionar correctamente. El UDT point también usa el espacio de nombres System.Text para trabajar con cadenas.
Nota:
Los objetos de base de datos de Visual C++, como udT, compilados con /clr:pure no se admiten para su ejecución.
Especificar atributos
Los atributos determinan el modo de usar la serialización para construir la representación de almacenamiento de los UDT y para transmitirlos por valor al cliente.
Se requiere Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute . El atributo Serializable es opcional. También puede especificar Microsoft.SqlServer.Server.SqlFacetAttribute para proporcionar información sobre el tipo de valor devuelto de un UDT. Para obtener más información, vea Atributos personalizados para las rutinas CLR.
Atributos del UDT Point
Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute establece el formato de almacenamiento del UDT de punto en nativo. IsByteOrdered se establece en true, lo que garantiza que los resultados de las comparaciones sean los mismos en SQL Server que si la misma comparación se hubiera realizado en código administrado. El UDT implementa la interfaz System.Data.SqlTypes.INullable para que el UDT sea compatible.
En el fragmento de código siguiente se muestran los atributos del UDT de punto .
<Serializable(), SqlUserDefinedTypeAttribute(Format.Native, _
IsByteOrdered:=True)> _
Public Structure Point
Implements INullable
[Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedType(Format.Native,
IsByteOrdered=true)]
public struct Point : INullable
{
Implementar la nulabilidad
Además de especificar correctamente los atributos de los ensamblados, el UDT también debe admitir la nulabilidad. Los UDT cargados en SQL Server son compatibles con valores NULL, pero para que el UDT reconozca un valor NULL, el UDT debe implementar la interfaz System.Data.SqlTypes.INullable .
Debe crear una propiedad denominada IsNull, que es necesaria para determinar si un valor es NULL desde el código CLR. Cuando SQL Server encuentra una instancia nula de un UDT, el UDT se conserva mediante métodos normales de control de valores NULL. El servidor no pierde tiempo serializando o deserializando el UDT sin necesidad y no desaprovecha espacio para almacenar un UDT NULL. Esta comprobación de valores NULL se realiza cada vez que se lleva un UDT desde CLR, lo que significa que el uso de la construcción IS NULL de Transact-SQL para comprobar si hay UDT null siempre debe funcionar. El servidor también usa la propiedad IsNull para probar si una instancia es null. Una vez que el servidor determina que el UDT es NULL, puede usar su propio control de valores NULL nativos.
El método get() de IsNull no tiene mayúsculas especiales de ninguna manera. Si una variable Point @p es Null, @p.IsNull se evaluará de forma predeterminada como "NULL", no "1". Esto se debe a que el atributo SqlMethod(OnNullCall) del método get() isNull tiene como valor predeterminado false. Dado que el objeto es Null, cuando se solicita la propiedad , no se deserializa el objeto, no se llama al método y se devuelve un valor predeterminado de "NULL".
Ejemplo
En el ejemplo siguiente, la variable is_Null
es privada y contiene el estado NULL para la instancia del UDT. El código debe mantener un valor adecuado para is_Null
. El UDT también debe tener una propiedad estática denominada Null que devuelva una instancia de valor NULL del UDT. Esto permite al UDT devolver un valor NULL si la instancia también es NULL en la base de datos.
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 frente a IsNull
Considere una tabla que contiene el esquema Points(id int, location Point), donde Point es un UDT clR y las siguientes consultas:
--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;
Ambas consultas devuelven los identificadores de puntos con ubicaciones que no son NULL . En la consulta 1 (Query 1), se usa el control habitual de valores NULL y no se requiere ninguna deserialización de UDT. La consulta 2, por otro lado, tiene que deserializar cada objeto distinto de Null y llamar a clR para obtener el valor de la propiedad IsNull . Claramente, el uso de IS NULL mostrará un mejor rendimiento y nunca debería haber una razón para leer la propiedad IsNull de un UDT desde código Transact-SQL.
Entonces, ¿cuál es el uso de la propiedad IsNull ? En primer lugar, es necesario determinar si un valor es Null desde el código CLR. En segundo lugar, el servidor necesita una manera de probar si una instancia es Null, por lo que el servidor usa esta propiedad. Una vez que determina que es Null, puede usar su control nativo null para controlarlo.
Implementar el método Parse
Los métodos Parse y ToString permiten conversiones hacia y desde representaciones de cadena del UDT. El método Parse permite convertir una cadena en un UDT. Debe declararse como estático (o Compartido en Visual Basic) y tomar un parámetro de tipo System.Data.SqlTypes.SqlString.
El código siguiente implementa el método Parse para point UDT, que separa las coordenadas X e Y. El método Parse tiene un único argumento de tipo System.Data.SqlTypes.SqlString y supone que los valores X e Y se proporcionan como una cadena delimitada por comas. Establecer el atributo Microsoft.SqlServer.Server.SqlMethodAttribute.OnNullCall en false impide que se llame al método Parse desde una instancia nula de Point.
<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;
}
Implementar el método ToString
El método ToString convierte el UDT de punto en un valor de cadena. En este caso, se devuelve la cadena "NULL" para una instancia Null del tipo Point . El método ToString invierte el método Parse mediante un System.Text.StringBuilder para devolver un System.String delimitado por comas que consta de los valores de coordenadaS X e Y. Dado que InvokeIfReceiverIsNull tiene como valor predeterminado false, la comprobación de una instancia nula de Point no es necesaria.
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();
}
}
Exponer las propiedades del UDT
El UDT point expone coordenadas X e Y que se implementan como propiedades públicas de lectura y escritura del tipo System.Int32.
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;
}
}
Validar los valores UDT
Al trabajar con datos UDT, SQL Server Motor de base de datos convierte automáticamente los valores binarios en valores UDT. Este proceso de conversión implica la comprobación de que los valores son adecuados para el formato de serialización del tipo, así como asegurarse de que el valor puede deserializarse correctamente. Esto garantiza que el valor se pueda convertir de nuevo en formato binario. En el caso de los UDT ordenados por bytes, esto también garantiza que el valor binario resultante coincida con el valor binario original. De esta forma, se evita que los valores que no son válidos se conserven en la base de datos. En algunos casos, este nivel de comprobación puede resultar inadecuado. Puede ser necesaria una validación adicional cuando se exige que los valores UDT se encuentren en un dominio o intervalo esperado. Por ejemplo, un UDT que implementa una fecha podría exigir que el valor de día sea un número positivo que pertenezca a un intervalo determinado de valores válidos.
La propiedad Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.ValidationMethodName de Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute permite proporcionar el nombre de un método de validación que el servidor ejecuta cuando los datos se asignan a un UDT o se convierten en udT. ValidationMethodName también se llama durante la ejecución de las operaciones de llamada a procedimiento remoto (RPC) de la utilidad bcp, BULK INSERT, DBCC CHECKDB, DBCC CHECKFILEGROUP, DBCC CHECKTABLE, consulta distribuida y llamada a procedimiento remoto (RPC). El valor predeterminado de ValidationMethodName es NULL, lo que indica que no hay ningún método de validación.
Ejemplo
El fragmento de código siguiente muestra la declaración de la clase Point , que especifica un ValidationMethodName de ValidatePoint.
<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
{
Si se especifica un método de validación, debe tener una firma de aspecto similar al fragmento de código siguiente.
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;
}
}
El método de validación puede tener cualquier ámbito y debe devolver true si el valor es válido y false en caso contrario. Si el método devuelve false o produce una excepción, el valor se trata como no válido y se genera un error.
En el ejemplo siguiente, el código solamente permite valores iguales a cero o mayores que las coordenadas X e Y.
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;
}
}
Limitaciones del método de validación
El servidor llama al método de validación cuando el servidor realiza conversiones, no cuando se insertan datos estableciendo propiedades individuales o cuando se insertan datos mediante una instrucción INSERT de Transact-SQL.
Debe llamar explícitamente al método de validación desde establecedores de propiedades y el método Parse si desea que el método de validación se ejecute en todas las situaciones. No se trata de ningún requisito e incluso, en algunos casos, es posible que no se desee.
Ejemplo de validación de Parse
Para asegurarse de que el método ValidatePoint se invoca en la clase Point , debe llamarlo desde el método Parse y desde los procedimientos de propiedad que establecen los valores de coordenadaS X e Y. El fragmento de código siguiente muestra cómo llamar al método de validación ValidatePoint desde la función Parse .
<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;
}
Ejemplo de validación de propiedad
El fragmento de código siguiente muestra cómo llamar al método de validación ValidatePoint desde los procedimientos de propiedad que establecen las coordenadas X e Y.
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.");
}
}
}
Codificar métodos UDT
Cuando codifique los métodos UDT, tenga en cuenta si el algoritmo usado podría cambiar con el tiempo. En ese caso, es posible que desee considerar la posibilidad de crear una clase independiente para los métodos que usa el UDT. Si cambia el algoritmo, puede volver a compilar la clase con el nuevo código y cargar el ensamblado en SQL Server sin afectar al UDT. En muchos casos, los UDT se pueden volver a cargar mediante la instrucción ALTER ASSEMBLY de Transact-SQL, pero esto podría causar problemas con los datos existentes. Por ejemplo, el UDT currency incluido con la base de datos de ejemplo AdventureWorks usa una función ConvertCurrency para convertir valores de moneda, que se implementa en una clase independiente. Es posible que en un futuro los algoritmos de conversión cambien de modo impredecible o que se necesite nueva funcionalidad. Separar la función ConvertCurrency de la implementación de Currency UDT proporciona mayor flexibilidad al planear cambios futuros.
Ejemplo
La clase Point contiene tres métodos simples para calcular la distancia: Distance, DistanceFrom y DistanceFromXY. Cada devuelve un cálculo doble de la distancia de Punto a cero, la distancia desde un punto especificado a Point y la distancia desde las coordenadas X e Y especificadas a Point. Distance y DistanceFrom cada llamada a DistanceFromXY y muestran cómo usar argumentos diferentes para cada método.
' 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));
}
Usar atributos SqlMethod
La clase Microsoft.SqlServer.Server.SqlMethodAttribute proporciona atributos personalizados que se pueden usar para marcar definiciones de método con el fin de especificar determinismo, comportamiento de llamada null y especificar si un método es un mutador. Se asume el uso de valores predeterminados para estas propiedades y solamente se usa el atributo personalizado cuando se necesita un valor no predeterminado.
Nota:
La clase SqlMethodAttribute hereda de la clase SqlFunctionAttribute , por lo que SqlMethodAttribute hereda los campos FillRowMethodName y TableDefinition de SqlFunctionAttribute. Esto significa que es posible escribir un método con valores de tabla, que no es el caso. El método compila y el ensamblado se implementa, pero se produce un error sobre el tipo de valor devuelto IEnumerable en tiempo de ejecución con el siguiente mensaje: "Method, property o field '<name>' in class>'< in assembly '<assembly>' has invalid return type".
En la tabla siguiente se describen algunas de las propiedades Microsoft.SqlServer.Server.SqlMethodAttribute pertinentes que se pueden usar en los métodos UDT y se enumeran sus valores predeterminados.
DataAccess
Indica si la función implica el acceso a los datos de usuario almacenados en la instancia local de SQL Server. El valor predeterminado es DataAccessKind.Ninguno.
IsDeterministic
Indica si la función genera los mismos valores de salida si se especifican los mismos valores de entrada y el mismo estado de la base de datos. El valor predeterminado es false.
IsMutator
Indica si el método produce un cambio de estado en la instancia del UDT. El valor predeterminado es false.
IsPrecise
Indica si la función implica cálculos imprecisos, como operaciones de punto flotante. El valor predeterminado es false.
OnNullCall
Indica si se llama al método cuando se especifican argumentos de entrada de referencia NULL. El valor predeterminado es true.
Ejemplo
La propiedad Microsoft.SqlServer.Server.SqlMethodAttribute.IsMutator permite marcar un método que permite un cambio en el estado de una instancia de un UDT. Transact-SQL no permite establecer dos propiedades UDT en la cláusula SET de una instrucción UPDATE. Sin embargo, puede tener un método marcado como mutador que cambie los dos miembros.
Nota:
No se permite el uso de métodos mutadores en las consultas. Solo puede llamarse a estos métodos en instrucciones de asignación o en instrucciones de modificación de datos. Si un método marcado como mutador no devuelve void (o no es un Sub en Visual Basic), CREATE TYPE produce un error.
En la siguiente instrucción se supone la existencia de un UDT de triángulos que tiene un método Rotate . La siguiente instrucción de actualización de Transact-SQL invoca el método Rotate :
UPDATE Triangles SET t.RotateY(0.6) WHERE id=5
El método Rotate está decorado con el valor del atributo SqlMethod IsMutator en true para que SQL Server pueda marcar el método como un método mutador. El código también establece OnNullCall en false, que indica al servidor que el método devuelve una referencia nula (Nothing en Visual Basic) si alguno de los parámetros de entrada son referencias nulas.
<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);
}
Implementar un UDT con un formato definido por el usuario
Al implementar un UDT con un formato definido por el usuario, debe implementar métodos de lectura y escritura que implementen la interfaz Microsoft.SqlServer.Server.IBinarySerialize para controlar la serialización y deserialización de datos UDT. También debe especificar la propiedad MaxByteSize de Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.
El UDT Currency
Currency UDT se incluye con los ejemplos clR que se pueden instalar con SQL Server, a partir de SQL Server 2005 (9.x).
El UDT de moneda admite el manejo de cantidades de dinero en el sistema monetario de una referencia cultural determinada. Debe definir dos campos: una cadena para CultureInfo, que especifica quién emitió la moneda (por ejemplo, en-us) y un decimal para CurrencyValue, la cantidad de dinero.
Aunque el servidor no lo usa para realizar comparaciones, currency UDT implementa la interfaz System.IComparable, que expone un único método, System.IComparable.CompareTo. Se usa del lado cliente en situaciones en las que se desean realizar comparaciones precisas u ordenar valores de moneda dentro de las referencias culturales.
El código que se ejecuta en CLR compara por separado la referencia cultural y el valor de moneda. En el caso del código transact-SQL, las siguientes acciones determinan la comparación:
Establezca el atributo IsByteOrdered en true, que indica a SQL Server que use la representación binaria persistente en el disco para realizar comparaciones.
Use el método Write para currency UDT para determinar cómo se conserva el UDT en el disco y, por tanto, cómo se comparan y ordenan los valores UDT para las operaciones de Transact-SQL.
Guarde el UDT de moneda con el siguiente formato binario:
Guarde la referencia cultural como una cadena codificada mediante UTF-16, con bytes del 0 al 19, que se rellena a la derecha con caracteres NULL.
Use el byte 20 y los bytes siguientes para almacenar el valor decimal de la moneda.
El propósito del relleno es asegurarse de que la referencia cultural está completamente separada del valor de moneda, de modo que, cuando un UDT se compara con otro en código Transact-SQL, los bytes de referencia cultural se comparan con los bytes de referencia cultural y los valores de bytes de moneda se comparan con los valores de bytes de moneda.
Atributos de Currency
Currency UDT se define con los atributos siguientes.
<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
{
Crear métodos de lectura y escritura con IBinarySerialize
Al elegir el formato de serialización UserDefined , también debe implementar la interfaz IBinarySerialize y crear sus propios métodos de lectura y escritura . Los procedimientos siguientes de Currency UDT usan System.IO.BinaryReader y System.IO.BinaryWriter para leer y escribir en el 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();
}
Consulte también
Comentarios
https://aka.ms/ContentUserFeedback.
Proximamente: Ao longo de 2024, retiraremos gradualmente GitHub Issues como mecanismo de comentarios sobre o contido e substituirémolo por un novo sistema de comentarios. Para obter máis información, consulte:Enviar e ver os comentarios