3

在 SQL 中它很容易,因为它支持 binary(10) LSN 值进行比较:

SELECT *, __$start_lsn, __$seqval
FROM cdc.fn_cdc_get_all_changes_dbo_sometable(@startLsn, @endLsn, 'all update old') 
WHERE __$seqval > @seqval 
ORDER BY __$start_lsn, __$seqval

在 C# 中更难:

byte[] mySeqval = ...
foreach(var row in cdcData)
{
    if(row.seqval > mySeqval) // Cannot perform this
        ...
}

是否可以将 LSN / SeqVal 值转换为可以轻松比较的数字?这些大小为 10 个字节(80 位)。

我的项目在 .Net 3.5 中

4

5 回答 5

4

当我回顾过去几周时,我很惊讶在任何地方都找不到这个问题的半体面的答案。

LSN 的主要问题是它们是 10 字节,因此不能简单地转换Int64和比较它们(顺便说一句:你真的会生成那么多 LSN 吗?! Int64真的很大)。正如 OP 发现的那样,逐个比较字节有点痛苦/容易出错(比较相等性很好 - 比较大于/小于小于)。但是,从 .Net Framework 4 开始,我们有了这个BigInteger类,可以用来轻松比较超过 8 个字节的整数。

所以问题只是如何将 varbinary(10) 从 LSN 获取到 BigInteger。从检查 [1] 看来,SQL 以大端格式存储 LSN,因此您必须:

  • 进入varbinary(10)记忆。LinqToSql 会给你Binary,其他提供者会直接映射到 byte[]。
  • 如果您使用的是小端架构(提示:您是),请翻转字节。IEnumerable.Reverse().ToArray()如果你不想自己做一个反向循环,会这样做
  • 称呼new BigInteger(bytes)
  • 闲暇时比较价值观

这可能看起来像这样:

// https://gist.github.com/piers7/91141f39715a2ec133e5
// Example of how to interpret SQL server CDC LSNs in C# / .Net
// This is required when polling a server for updates in order to determine
// if a previously stored LSN is still valid (ie > min LSN available)

// Requires .Net 4 (or equivilent BigInteger implementation)
// Sample is a Linqpad script, but you get the idea

// NB: That SQL uses big-endian representation for it's LSNs is not
// (as best I know) something they guarantee not to change

Connection.Open();
var command = Connection.CreateCommand();
command.CommandText = @"select sys.fn_cdc_get_max_lsn() as maxLsn";
var bytes = (byte[])command.ExecuteScalar();

// dump bytes as hex
var hexString = string.Join(" ", bytes.Select(b => b.ToString("X2")))
    .Dump("Hex String");

if(BitConverter.IsLittleEndian)
    bytes = bytes.Reverse().ToArray();

var bigInt = new BigInteger(bytes)
    // dump Integer representation
    .Dump("Big Integer")
;

[1] 我进行了顺序更改,并查看了 LSN。最后一个字节显然是递增的,因此是大端的。

于 2015-05-12T14:21:32.183 回答
1

目前正在调查http://intx.codeplex.com/作为其 .Net 2.0

于 2011-03-30T10:46:42.507 回答
1

最后写了自己的 LSN 比较器:

public class CdcLsnValue : IEquatable<CdcLsnValue>
{
    public byte[] Bytes;
    private const int Size = 10;

    public CdcLsnValue()
    {
        Bytes = null;
    }

    public CdcLsnValue(byte[] lsn)
    {
        if (lsn == null)
        {
            Bytes = null;
            return;
        }
        if(lsn.Length != Size)
            throw new ArgumentOutOfRangeException("lsn");
        Bytes = (byte[]) lsn.Clone();
    }

    public static bool operator ==(CdcLsnValue left, CdcLsnValue right)
    {
        if (ReferenceEquals(left, right)) return true;
        if (ReferenceEquals(null, left)) return false;
        if (ReferenceEquals(null, right)) return false;

        for (int i = 0; i < Size; i++)
        {
            if (left.Bytes[i] == right.Bytes[i])
                continue;
            return false;
        }
        return true;

    }

    public static bool operator !=(CdcLsnValue left, CdcLsnValue right)
    {
        return !(left == right);
    }

    public static bool operator <=(CdcLsnValue left, CdcLsnValue right)
    {
        if (ReferenceEquals(null, left)) return false;
        if (ReferenceEquals(null, right)) return false;

        for (int i = 0; i < Size; i++)
        {
            if (left.Bytes[i] <= right.Bytes[i])
                continue;
            return false;
        }
        return true;
    }

    public static bool operator >=(CdcLsnValue left, CdcLsnValue right)
    {
        if (ReferenceEquals(null, left)) return false;
        if (ReferenceEquals(null, right)) return false;

        for (int i = 0; i < Size; i++)
        {
            if (left.Bytes[i] >= right.Bytes[i])
                continue;
            return false;
        }
        return true;
    }

    public static bool operator <(CdcLsnValue left, CdcLsnValue right)
    {
        if (ReferenceEquals(null, left)) return false;
        if (ReferenceEquals(null, right)) return false;

        if (left == right)
            return false;

        return left <= right;
    }

    public static bool operator >(CdcLsnValue left, CdcLsnValue right)
    {
        return !(left < right);
    }

    public bool Equals(CdcLsnValue other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other.Bytes, Bytes);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof(CdcLsnValue)) return false;
        return Equals((CdcLsnValue)obj);
    }

    public override int GetHashCode()
    {
        return (Bytes != null ? Bytes.GetHashCode() : 0);
    }
}
于 2011-03-30T11:46:30.527 回答
0

最终不需要使用上述任何一种。我的一位同事最终解决了这个问题(感谢 Tony Broodie)。这样做的方法是与 seqval 进行比较,然后取 +1。简单的。

SqlExecutor.ExecuteReader(cnn,
string.Format("SELECT {0} , __$start_lsn, __$seqval , __$update_mask " +
    "FROM cdc.fn_cdc_get_all_changes_{1}(@startLsn,@endLsn,'all update old') cdc {2} " +
    "where __$operation = {3} ORDER BY __$start_lsn, __$seqval", columns,
    captureInstance, joins, (int)operation), 
    reader =>
    {
        if (reader != null)
            items.Add(createEntity(reader));
    }, 5, 60, new SqlParameter("@startLsn", lsn), 
              new SqlParameter("@endLsn", endLsn));
});
startLsn = lsn;
seqVal = sequence;
var startIndex = sequence == null ? 0 : 
  items.FindIndex(0, item => item.Lsn.SequenceEqual(lsn)
    && item.Seqval.SequenceEqual(sequence)) + 1; // <---- Look here. See the +1
return items.Skip(startIndex).ToList();
于 2011-04-12T09:44:09.663 回答
0

受@casperOne 的固定长度解决方案的启发,https: //stackoverflow.com/a/10658931/8478013我创建了一个扩展方法,允许您按字节数组排序,如下所示:

allchanges.OrderByDescendingFixedLength(sortLength: 10, x => x.___seqval)

这是扩展方法:

public static IEnumerable<T> OrderByFixedLength<T>(this IEnumerable<T> items, int sortLength, Func<T, byte[]> fieldValue)
    {
        //this routine came from:
        //      https://stackoverflow.com/questions/10658709/linq-orderbybyte-values
        //  it was modified to be generic <T> instead of specific type

        // Validate parameters.
        if (items == null) throw new ArgumentNullException("items");
        if (sortLength < 0) throw
            new ArgumentOutOfRangeException("sortLength", sortLength,
                "The sortLength parameter must be a non-negative value.");

        // Shortcut, if sortLength is zero, return the sequence, as-is.
        if (sortLength == 0) return items;

        // The ordered enumerable.
        IOrderedEnumerable<T> ordered = items.OrderBy(x => fieldValue(x)[0]);

        // Cycle from the second index on.
        for (int index = 1; index < sortLength; index++)
        {
            // Copy the index.
            int indexCopy = index;

            // Sort by the next item in the array.
            ordered = ordered.ThenBy(x => fieldValue(x)[indexCopy]);
        }

        // Return the ordered enumerable.
        return ordered;
    }

  public static IEnumerable<T> OrderByDescendingFixedLength<T>(this IEnumerable<T> items, int sortLength, Func<T, byte[]> fieldValue)
    {
        //we could probably optimize this, but honestly it's used so little and already quite quick... so we'll just go with it
        return items.OrderByFixedLength(sortLength, fieldValue).Reverse();
    }
于 2019-12-30T14:17:51.527 回答