【问题标题】:How to properly serialize a sortable SQLCLR User-Defined Type?如何正确序列化可排序的 SQLCLR 用户定义类型?
【发布时间】:2013-07-27 08:33:46
【问题描述】:

我正在 C# 中构建一个自定义的用户定义类型以用于 SQL CLR。 See this reference.

基础数据可以使用一个常规的有符号 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 个字节的值,其中零看起来像 0x8000000000 和 -1 看起来像 0x7FFFFFFF00。它像我一样自动转换,但它为 IsNull 布尔字段存储了一个额外的字节。它不会接受将其标记为[NonSerialized],并且不会让我删除它。可空性是一项要求,因此必须存在。

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

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

另请参阅 DBA.SE 上的 related (but different) question

【问题讨论】:

    标签: c# .net sql-server sorting sqlclr


    【解决方案1】:

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

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

    【讨论】:

    • 那会很好,但是查看您链接的文档,在第一个“注释”框中,它说IComparable 未被服务器使用。它只用于客户端的东西。在查询列中包含此 UDT 的表时,我最感兴趣的是排序是否正确。
    • 啊,你说得对。对不起,没有帮助。您可以尝试使用 Format.Native,这可能会强制它序列化已签名的整数,以便它们是可排序的(就像您自己做的那样)。
    • 至于它存储 NULL 字节,如果你的类型真的不能是 NULL,你可以尝试创建 IsNull 和 Null 属性,这样它们就不会引用任何东西,只有 get with没有套装。
    猜你喜欢
    • 2022-07-06
    • 2020-03-02
    • 2011-11-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-03
    • 2021-11-19
    • 1970-01-01
    • 2010-09-19
    相关资源
    最近更新 更多