2013-07-26 21 views
5

Sto costruendo un tipo personalizzato definito dall'utente in C# per l'utilizzo con SQL CLR. See this reference.Come serializzare correttamente un SQLCLR ordinabile di tipo definito dall'utente?

I dati sottostanti possono essere rappresentati in 4 byte utilizzando un numero intero a 32 bit con segno regolare. Ecco l'implementazione:

[Serializable] 
[SqlUserDefinedType(Format.UserDefined, IsByteOrdered = true, 
        IsFixedLength = true, MaxByteSize = 4)] 
public struct MyUDT : INullable, IBinarySerialize 
{ 
    private int _value; 

    public int Value 
    { 
     get { return _value; } 
    } 

    public MyUDT(int value) 
     : this() 
    { 
     _value = value; 
    } 

    public override string ToString() 
    { 
     return _value.ToString(CultureInfo.InvariantCulture); 
    } 

    [SqlMethod(OnNullCall = false)] 
    public static MyUDT Parse(SqlString s) 
    { 
     int i = int.Parse(s.Value); 
     return new MyUDT(i); 
    } 

    public bool IsNull { get; private set; } 
    public static MyUDT Null 
    { 
     get { return new MyUDT { IsNull = true }; } 
    } 

    public void Read(BinaryReader r) 
    { 
     var bytes = r.ReadBytes(4); 

     if (BitConverter.IsLittleEndian) 
      Array.Reverse(bytes); 

     int i = BitConverter.ToInt32(bytes, 0); 
     _value = i^unchecked((int) 0x80000000); 
    } 

    public void Write(BinaryWriter w) 
    { 
     int i = _value^unchecked((int) 0x80000000); 
     var bytes = BitConverter.GetBytes(i); 

     if (BitConverter.IsLittleEndian) 
      Array.Reverse(bytes); 

     w.Write(bytes); 
    } 
} 

Naturalmente, vorrei aggiungere altri metodi che renderanno questo più utile, ma questo è il minimo che si compila e pubblica.

Ho bisogno di assicurarmi che quando questo tipo viene usato per confronti, come quando è una colonna in una tabella, sia ordinato correttamente. Ci saranno sia valori positivi che negativi.

Come si può vedere, ho dovuto invertire l'ordine dei byte, perché il endianness è al contrario. Ho anche dovuto regolare i byte in modo che lo zero sia rappresentato come 0x80000000 e -1 diventa 0x7FFFFFFF. Altrimenti i valori non vengono ordinati correttamente.

Tuttavia, questo è non normalmente come funzionano gli interi di SQL Server. Ad esempio:

SELECT convert(varbinary, -16) -- 0xFFFFFFF0 
SELECT convert(varbinary, 16) -- 0x00000010 

Eppure, questo tipo va bene! Quindi posso solo concludere che esistono altri tipi di ordinamento oltre al semplice ordine dei byte. Ma non riesco a trovare alcun modo per controllarlo nel mio UDT. È possibile?

Tutti gli esempi che ho trovato mostrare qualcosa di simile:

public void Read(BinaryReader r) 
{ 
    _value = r.ReadInt32(); 
} 

public void Write(BinaryWriter w) 
{ 
    w.Write(_value); 
} 

Ma che scrive in un formato completamente diverso. È simile al formato di SQL Server, ma il endianness è invertito (1 è 0x01000000 e -16 è 0xF0FFFFFF). Quindi le cose vengono completamente sbagliate. Quindi gli esempi sono sbagliati? C'è un bug da qualche parte?

Ho anche considerato l'utilizzo di Format.Native e l'omissione della serializzazione personalizzata. Questo tipo di opere, ma finisco con 5 valori di byte, dove zero assomiglia a 0x8000000000 e -1 assomiglia a 0x7FFFFFFF00. Si sta convertendo automaticamente come ho fatto io, ma memorizza un byte extra per il campo booleano IsNull. Non accetterà di avere quello contrassegnato come [NonSerialized] e non mi permetterà di rimuoverlo. Nullability è un requisito, quindi deve essere lì.

Sicuramente c'è un modo più semplice di dover eseguire la manipolazione dei bit?

Un'altra preoccupazione è che il mio tipo non è direttamente convertibile in uno int all'interno di SQL. Anche se, non sono sicuro di averne bisogno o meno.

Vedere anche, a related (but different) question su DBA.SE.

risposta

0

Sospetto che vogliate implementare IComparable e impostare IsByteOrdered su false. Ciò eliminerebbe la necessità di giocare con la conversione da zero a 0x80000000 e da -1 a 07FFFFFFF.

Finisco col dire, non sono nemmeno un principiante in questa roba.Non l'ho mai fatto da solo, ma ho dato un'occhiata alla documentazione: http://msdn.microsoft.com/en-us/library/ms131082.aspx

+0

Sarebbe molto bello, ma guardando i documenti che hai collegato, nella prima casella "Note", si dice che 'IComparable' non è usato dal server. È solo lì per cose sul lato client. Sono principalmente interessato all'ordinamento corretto quando si esegue una query su una tabella con questo UDT in una colonna. –

+0

Ah, hai ragione. Scusa, non è stato d'aiuto. Potresti provare a utilizzare Format.Native, che potrebbe obbligarlo a serializzare le firme firmate in modo da renderle ordinabili (in pratica come fai tu stesso). –

+0

Per quanto riguarda la memorizzazione del byte NULL, se il tuo tipo non può mai essere NULL, puoi provare a creare le proprietà IsNull e Null in modo che non facciano riferimento a nulla e ottengano solo set senza. –

Problemi correlati