如何在C#中比较SQL Server CDC LSN值?

时间:2011-03-29 14:55:16

标签: c# sql-server-2008 change-data-capture

在SQL中它很容易,因为它支持二进制(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

5 个答案:

答案 0 :(得分:2)

在过去的几周里,当我看到这个问题时,我很惊讶没有找到对这个问题的一个不错的答案。

LSN的主要问题是它们是10个字节,所以它们不能简单地转换为random.seed(str(seed1))并进行比较(除此之外:你真的会生成那么多LSN吗?! Int64非常大。正如OP发现的那样,逐个比较字节有点痛苦/容易出错(比较相等是好的 - 比较大于/小于小于等于)。但是,从.Net Framework 4开始,我们有Int64类,可用于轻松比较超过8个字节的整数。

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

  • BigInteger放入内存中。 LinqToSql将为您提供varbinary(10),其他提供程序将直接映射到byte []。
  • 翻转字节,如果你是在little-endian架构上(提示:你是)。如果您不想自己进行反向循环,Binary会这样做
  • 致电IEnumerable.Reverse().ToArray()
  • 比较闲暇时的价值

这看起来像这样:

new BigInteger(bytes)

[1]我进行了连续更改,并查看了LSN。最后一个字节显然是递增的,因此是big-endian。

答案 1 :(得分:1)

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

答案 2 :(得分: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);
    }
}

答案 3 :(得分:0)

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

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();

答案 4 :(得分: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();
    }