2011-03-29 82 views
2

在SQL其容易,因为它支持二进制(10)LSN为比较值:如何比较C#中的SQL Server CDC 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

回答

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(); 
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); 
    } 
} 
+0

它几乎可以工作,但不是100%。所以请不要按原样使用它。 – 2011-04-12 09:35:01

1

我很惊讶没有在过去几周看过这个问题的任何地方找到一个半正确的答案。

的主要问题LSN的是,他们是10个字节,所以不能简单地转化为Int64比较(题外话:你真的产生很多的LSN?Int64真大)。正如OP发现的,逐字节地比较字节有点痛苦/容易出错(比较相等性很好 - 比较大于/小于 - 小于 - )。但是,从.Net Framework 4开始,我们有BigInteger类,它可以用来轻松比较超过8个字节的整数。

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

  • 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。最后一个字节显然是递增的,因此是big-endian。