Criar tipos definidos pelo usuário – Codificação

Aplica-se a:SQL Server

Ao codificar a definição UDT (tipo definido pelo usuário), você deve implementar vários recursos, dependendo da implementação da UDT como classe ou estrutura, bem como das opções de formato e de serialização escolhidas.

O exemplo nesta seção ilustra a implementação de um UDT de Ponto como um struct (ou Estrutura no Visual Basic). O UDT point consiste em coordenadas X e Y implementadas como procedimentos de propriedade.

Os seguintes namespaces são obrigatórios na definição de uma UDT:

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

O namespace Microsoft.SqlServer.Server contém os objetos necessários para vários atributos do UDT e o namespace System.Data.SqlTypes contém as classes que representam SQL Server tipos de dados nativos disponíveis para o assembly. É claro que talvez haja namespaces adicionais necessários ao funcionamento correto do assembly. O UDT de Ponto também usa o namespace System.Text para trabalhar com cadeias de caracteres.

Observação

Não há suporte para objetos de banco de dados do Visual C++, como UDTs, compilados com /clr:pure para execução.

Especificando atributos

Os atributos determinam como a serialização é usada para construir a representação de armazenamento de UDTs e transmiti-los por valor para o cliente.

O Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute é necessário. O atributo Serializable é opcional. Você também pode especificar o Microsoft.SqlServer.Server.SqlFacetAttribute para fornecer informações sobre o tipo de retorno de um UDT. Para obter mais informações, confira Atributos personalizados para rotinas do CLR (Common Language Runtime).

Atributos da UDT Point

O Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute define o formato de armazenamento do UDT de Ponto como Nativo. IsByteOrdered é definido como true, o que garante que os resultados das comparações sejam os mesmos em SQL Server como se a mesma comparação tivesse ocorrido no código gerenciado. O UDT implementa a interface System.Data.SqlTypes.INullable para tornar o UDT ciente nulo.

O fragmento de código a seguir mostra os atributos do UDT de Ponto .

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

Implementando a nulidade

Além de especificar corretamente os atributos dos assemblies, a UDT também precisa oferecer suporte à nulidade. Os UDTs carregados em SQL Server têm reconhecimento nulo, mas para que o UDT reconheça um valor nulo, o UDT deve implementar a interface System.Data.SqlTypes.INullable.

Você deve criar uma propriedade chamada IsNull, que é necessária para determinar se um valor é nulo de dentro do código CLR. Quando SQL Server encontra uma instância nula de um UDT, o UDT é persistente usando métodos normais de tratamento nulo. O servidor não perde tempo serializando ou desserializando a UDT caso não precise, e ele não perde espaço armazenando uma UDT nula. Esse marcar para nulos é executado sempre que um UDT é trazido do CLR, o que significa que o uso do constructo NULL is do Transact-SQL para marcar para UDTs nulos sempre deve funcionar. A propriedade IsNull também é usada pelo servidor para testar se uma instância é nula. Quando determina que a UDT é nula, o servidor pode usar a manipulação de nulos nativa.

O método get() de IsNull não é de forma especial. Se uma variável Point@p for Null, @p.IsNull , por padrão, será avaliada como "NULL", não "1". Isso ocorre porque o atributo SqlMethod(OnNullCall) do método get() IsNull usa como padrão false. Como o objeto é Null, quando a propriedade é solicitada, o objeto não é desserializado, o método não é chamado e um valor padrão de "NULL" é retornado.

Exemplo

No seguinte exemplo, a variável is_Null é privada e mantém o estado de nulidade para a instância da UDT. O código deve manter um valor apropriado para is_Null. O UDT também deve ter uma propriedade estática chamada Null que retorna uma instância de valor nulo do UDT. Isso permite que a UDT retorne um valor nulo caso a instância seja realmente nula no banco de dados.

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 X IsNull

Considere uma tabela que contém o esquema Points(id int, location Point), em que Point é um UDT CLR e as seguintes 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 as consultas retornam as IDs de pontos com locais não nulos . Na Consulta 1, é usada a manipulação de nulos normal, não havendo nenhuma desserialização das UDTs obrigatória. A consulta 2, por outro lado, precisa desserializar cada objeto não Null e chamar para o CLR para obter o valor da propriedade IsNull . Claramente, o uso de IS NULL apresentará melhor desempenho e nunca deve haver um motivo para ler a propriedade IsNull de um UDT do código Transact-SQL.

Então, qual é o uso da propriedade IsNull ? Primeiro, é necessário determinar se um valor é Null de dentro do código CLR. Em segundo lugar, o servidor precisa de uma maneira de testar se uma instância é nula, portanto, essa propriedade é usada pelo servidor. Depois de determinar que ele é Null, ele pode usar seu tratamento nulo nativo para lidar com ele.

Implementando o método Parse

Os métodos Parse e ToString permitem conversões de e para representações de cadeia de caracteres do UDT. O método Parse permite que uma cadeia de caracteres seja convertida em um UDT. Ele deve ser declarado como estático (ou Compartilhado no Visual Basic) e usar um parâmetro do tipo System.Data.SqlTypes.SqlString.

O código a seguir implementa o método Parse para o UDT de Ponto , que separa as coordenadas X e Y. O método Parse tem um único argumento do tipo System.Data.SqlTypes.SqlString e pressupõe que os valores X e Y sejam fornecidos como uma cadeia de caracteres delimitada por vírgulas. Definir o atributo Microsoft.SqlServer.Server.SqlMethodAttribute.OnNullCall como false impede que o método Parse seja chamado de uma instância nula do 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;  
}  

Implementando o método ToString

O método ToString converte o UDT de Ponto em um valor de cadeia de caracteres. Nesse caso, a cadeia de caracteres "NULL" é retornada para uma instância Null do tipo Point . O método ToString inverte o método Parse usando um System.Text.StringBuilder para retornar um System.String delimitado por vírgulas que consiste nos valores das coordenadas X e Y. Como InvokeIfReceiverIsNull usa como padrão false, a marcar para uma instância nula de Point é desnecessária.

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();  
    }  
}  

Expondo propriedades da UDT

O Point UDT expõe coordenadas X e Y implementadas como propriedades públicas de leitura/gravação do 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;  
    }  
}  

Validando valores de UDT

Ao trabalhar com dados UDT, SQL Server Mecanismo de Banco de Dados converte automaticamente valores binários em valores UDT. Esse processo de conversão envolve verificar se os valores são apropriados ao formato de serialização do tipo e garantir que o valor possa ser desserializado corretamente. Isso assegura que o valor possa ser novamente convertido em um formato binário. No caso das UDTs ordenadas por byte, isso também garante que o valor binário resultante corresponda ao valor binário original. Isso impede a manutenção de valores inválidos no banco de dados. Em alguns casos, esse nível de verificação pode ser inadequado. Talvez seja obrigatória uma validação adicional quando os valores de UDT devem estar em um domínio ou intervalo esperado. Por exemplo, uma UDT que implementa uma data pode exigir que o valor de dia seja um número positivo e que esteja em um determinado intervalo de valores válidos.

A propriedade Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.ValidationMethodName da propriedade Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute permite fornecer o nome de um método de validação executado pelo servidor quando os dados são atribuídos a um UDT ou convertidos em um UDT. ValidationMethodName também é chamado durante a execução do utilitário bcp, BULK INSERT, DBCC CHECKDB, DBCC CHECKFILEGROUP, DBCC CHECKTABLE, consulta distribuída e operações de RPC (chamada de procedimento remoto) do TDS. O valor padrão de ValidationMethodName é nulo, indicando que não há nenhum método de validação.

Exemplo

O fragmento de código a seguir mostra a declaração da classe Point , que especifica um 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  
{  

Caso seja especificado, um método de validação deve ter uma assinatura semelhante ao seguinte fragmento de código.

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;  
    }  
}  

O método de validação pode ter qualquer escopo e deve retornar true se o valor for válido e false caso contrário. Se o método retornar false ou gerar uma exceção, o valor será tratado como não válido e um erro será gerado.

No exemplo abaixo, o código só permite valores iguais a zero ou maiores que as 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;  
    }  
}  

Limitações do método de validação

O servidor chama o método de validação quando o servidor está executando conversões, não quando os dados são inseridos definindo propriedades individuais ou quando os dados são inseridos usando uma instrução TRANSact-SQL INSERT.

Você deve chamar explicitamente o método de validação de setters de propriedade e o método Parse se quiser que o método de validação seja executado em todas as situações. Não se trata de um requisito e, em alguns casos, talvez não seja sequer desejável.

Exemplo de validação de Parse

Para garantir que o método ValidatePoint seja invocado na classe Point , você deve chamá-lo do método Parse e dos procedimentos de propriedade que definem os valores de coordenada X e Y. O fragmento de código a seguir mostra como chamar o método de validação ValidatePoint da função 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;  
}  

Exemplo de validação da propriedade

O fragmento de código a seguir mostra como chamar o método de validação ValidatePoint dos procedimentos de propriedade que definem as 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.");  
        }  
    }  
}  

Codificando métodos de UDT

Durante a codificação dos métodos de UDT, considere se o algoritmo usado pode ser alterado com o passar do tempo. Em caso positivo, você talvez queira considerar a criação de uma classe separada para os métodos usados pela UDT. Se o algoritmo for alterado, você poderá recompilar a classe com o novo código e carregar o assembly em SQL Server sem afetar o UDT. Em muitos casos, os UDTs podem ser recarregados usando a instrução ALTER ASSEMBLY do Transact-SQL, mas isso pode causar problemas com os dados existentes. Por exemplo, o Conversor de Moedas UDT incluído no banco de dados de exemplo AdventureWorks usa uma função ConvertCurrency para converter valores de moeda, que é implementada em uma classe separada. É possível que algoritmos de conversão sejam alterados de maneiras imprevisíveis no futuro, ou que uma nova funcionalidade seja obrigatória. Separar a função ConvertCurrency da implementação de UDT Conversor de Moedas fornece maior flexibilidade ao planejar alterações futuras.

Exemplo

A classe Point contém três métodos simples para calcular a distância: Distance, DistanceFrom e DistanceFromXY. Cada um retorna um cálculo duplo da distância de Ponto a zero, a distância de um ponto especificado até Point e a distância das coordenadas X e Y especificadas até Point. Distance e DistanceFrom cada chamada DistanceFromXY e demonstram como 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));  
}  

Usando atributos SqlMethod

A classe Microsoft.SqlServer.Server.SqlMethodAttribute fornece atributos personalizados que podem ser usados para marcar definições de método para especificar determinismo, no comportamento de chamada nula e especificar se um método é um modificador. Os valores padrão dessas propriedades são pressupostos, e o atributo personalizado só é usado quando um valor não padrão é necessário.

Observação

A classe SqlMethodAttribute herda da classe SqlFunctionAttribute , portanto , SqlMethodAttribute herda os campos FillRowMethodName e TableDefinition de SqlFunctionAttribute. Isso indica que é possível escrever um método com valor de tabela, o que não é o caso. O método compila e o assembly é implantado, mas um erro sobre o tipo de retorno IEnumerable é gerado em runtime com a seguinte mensagem: "Método, propriedade ou campo '<name>' na classe '<class>' no assembly '<assembly>' tem tipo de retorno inválido."

A tabela a seguir descreve algumas das propriedades relevantes de Microsoft.SqlServer.Server.SqlMethodAttribute que podem ser usadas em métodos UDT e lista seus valores padrão.

DataAccess
Indica se a função envolve o acesso a dados do usuário armazenados na instância local do SQL Server. O padrão é DataAccessKind. Nenhum.

IsDeterministic
Indica se a função produz os mesmos valores de saída, dados os mesmos valores de entrada e o mesmo estado de banco de dados. O padrão é false.

IsMutator
Indica se o método causa uma alteração de estado na instância da UDT. O padrão é false.

IsPrecise
Indica se a função envolve computações imprecisas como, por exemplo, operações de ponto flutuante. O padrão é false.

OnNullCall
Indica se o método é chamado quando são especificados argumentos de entrada de referência nulos. O padrão é true.

Exemplo

A propriedade Microsoft.SqlServer.Server.SqlMethodAttribute.IsMutator permite marcar um método que permite uma alteração no estado de uma instância de um UDT. O Transact-SQL não permite que você defina duas propriedades UDT na cláusula SET de uma instrução UPDATE. No entanto, é possível ter um método marcado como um modificador que altera os dois membros.

Observação

Os métodos Mutator não são permitidos em consultas. Eles só podem ser chamados em instruções de atribuição ou de modificação de dados. Se um método marcado como modificador não retornar void (ou não for um Sub no Visual Basic), CREATE TYPE falhará com um erro.

A instrução a seguir pressupõe a existência de um UDT triangles que tenha um método Rotate . A seguinte instrução de atualização Transact-SQL invoca o método Rotate :

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

O método Rotate é decorado com a configuração do atributo SqlMethodIsMutator como true para que SQL Server possa marcar o método como um método modificador. O código também define OnNullCall como false, o que indica ao servidor que o método retorna uma referência nula (Nothing no Visual Basic) se qualquer um dos parâmetros de entrada for referência nula.

<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);  
}  

Implementando uma UDT com um formato definido pelo usuário

Ao implementar um UDT com um formato definido pelo usuário, você deve implementar métodos de Leitura e Gravação que implementam a interface Microsoft.SqlServer.Server.IBinarySerialize para lidar com a serialização e desserialização de dados UDT. Você também deve especificar a propriedade MaxByteSize do Microsoft.SqlServer.Server.SqlUserDefinedTypeAttribute.

A UDT Currency

O Conversor de Moedas UDT está incluído com os exemplos clr que podem ser instalados com SQL Server, começando com SQL Server 2005 (9.x).

O Conversor de Moedas UDT dá suporte ao tratamento de quantidades de dinheiro no sistema monetário de uma cultura específica. Você deve definir dois campos: uma cadeia de caracteres para CultureInfo, que especifica quem emitiu a moeda (en-us, por exemplo) e um decimal para CurrencyValue, a quantidade de dinheiro.

Embora não seja usado pelo servidor para executar comparações, o Conversor de Moedas UDT implementa a interface System.IComparable, que expõe um único método, System.IComparable.CompareTo. Isso é usado do lado de cliente, em situações nas quais é desejável comparar com precisão ou ordenar valores de moeda dentro de culturas.

O código em execução no CLR compara a cultura separadamente do valor de moeda. Para o código Transact-SQL, as seguintes ações determinam a comparação:

  1. Defina o atributo IsByteOrdered como true, que informa SQL Server usar a representação binária persistente no disco para comparações.

  2. Use o método Write para o Conversor de Moedas UDT para determinar como o UDT é persistido no disco e, portanto, como os valores UDT são comparados e ordenados para operações Transact-SQL.

  3. Salve o Conversor de Moedas UDT usando o seguinte formato binário:

    1. Salve a cultura como uma cadeia de caracteres codificada UTF-16 para bytes de 0 a 19 com preenchimento à direita com caracteres nulos.

    2. Use bytes 20 e acima para conter o valor decimal da moeda.

A finalidade do preenchimento é garantir que a cultura seja completamente separada do valor da moeda, de modo que, quando um UDT for comparado com outro no código Transact-SQL, os bytes de cultura sejam comparados com os bytes de cultura e os valores de bytes de moeda sejam comparados com os valores de bytes de moeda.

Atributos Currency

O Conversor de Moedas UDT é definido com os atributos a seguir.

<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  
    {  

Criando métodos Read e Write com IBinarySerialize

Ao escolher o formato de serialização UserDefined , você também deve implementar a interface IBinarySerialize e criar seus próprios métodos de Leitura e Gravação . Os procedimentos a seguir do Conversor de Moedas UDT usam System.IO.BinaryReader e System.IO.BinaryWriter para ler e gravar no 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 Também

Criando um tipo de User-Defined