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.
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. –
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). –
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. –