2011-03-29 22 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。