在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
答案 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 []。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();
}