如何正确序列化可排序的SQLCLR用户定义类型

本文关键字:SQLCLR 用户 定义 类型 排序 何正确 序列化 | 更新日期: 2023-09-27 18:25:10

我正在C#中构建一个用于SQL CLR的自定义用户定义类型。请参阅本参考资料。

底层数据可以使用一个正则有符号的32位整数以4个字节表示。以下是实现:

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

当然,我将添加其他方法,使其更加有用,但这是编译和发布的最低要求。

我需要确保当这种类型用于比较时,例如当它是表中的列时,它的排序是正确的。将同时存在正值和负值。

正如您所看到的,我不得不颠倒字节顺序,因为字节序是向后的。我还必须调整字节,使零表示为0x80000000,-1变为0x7FFFFFFF。否则,值将无法正确排序。

但是,这不是SQL Server整数正常工作的方式。例如:

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

然而,这些都很好!所以我只能得出结论,除了原始字节顺序之外,还有其他排序类型。但我找不到任何方法来控制我的UDT。有可能吗?

我发现的所有例子都是这样的:

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

但这是用一种完全不同的格式写的。这与SQL Server的格式类似,但字节序相反(1是0x01000000,-16是0xF0FFFFFF)。所以事情完全错了。那么这些例子错了吗?哪里有虫子吗?

我还考虑过使用Format.Native并省略自定义序列化。这是可行的,但我最终得到了5个字节的值,其中0看起来像0x8000000000,-1看起来像0x7FFFFFFF00。它和我一样自动转换,但它为IsNull布尔字段存储了一个额外的字节。它不会接受将其标记为[NonSerialized],也不会让我删除它。可空性是一个要求,所以它必须存在。

肯定有比比特操作更简单的方法吗?

另一个问题是,我的类型不能直接转换为SQL中的int。不过,我不确定我是否需要。

另请参阅DBA.SE.

如何正确序列化可排序的SQLCLR用户定义类型

上的一个相关(但不同)问题

我怀疑您想要实现IComparable并将IsByteOrdered设置为false。这将通过将零转换为0x80000000和将-1转换为07FFFFFFF来消除玩游戏的需要。

我最后会说,我甚至不是这方面的初学者。我自己从来没有做过,但我浏览了一下这里的文档:http://msdn.microsoft.com/en-us/library/ms131082.aspx