我有一个需要处理几个需要同步/备份的表的要求。所有这些表的类都实现ITrackModifiedDate
:
interface ITrackModifiedDate
{
DateTime ModifiedDate { get; set; }
}
我需要分批处理这些内容,这意味着我需要为上次到达的位置添加书签。因此,我可以按ModifiedDate
进行排序,并只跟踪最后一个ModifiedDate
。但是该计划中存在一个缺陷:多个记录具有相同的ModifiedDate
很容易发生。这意味着我需要一个辅助标识符,而我通常可以唯一依赖的是关键字段,它始终是Guid
。
我从here的Expression
那里得到了一些帮助,但是当我尝试修改以添加“大于”子句时,事情就解决了。
async Task<ICollection<T>> GetModifiedRecords<T>(DateTime modifiedSince, Guid lastId) where T : class, ITrackModifiedDate
{
var parameterExp = Expression.Parameter(typeof(T), "x");
var propertyExp = Expression.Property(parameterExp, keyField);
var target = Expression.Constant(lastId);
var greaterThanMethod = Expression.GreaterThan(propertyExp, target);
var lambda = Expression.Lambda<Func<T, bool>>(greaterThanMethod, parameterExp);
var query = db.Set<T>()
.Where(t => t.ModifiedDate > modifiedSince ||
t.ModifiedDate == modifiedSince && lambda.Invoke(t));
var orderByExp = Expression.Lambda(propertyExp, parameterExp);
var thenByMethodGeneric = typeof(Queryable)
.GetMethods()
.Single(mi => mi.Name == "ThenBy" && mi.GetParameters().Length == 2);
var thenByMethod = thenByMethodGeneric.MakeGenericMethod(typeof(T), propertyExp.Type);
// first order by date, then id
query = query.OrderBy(t => t.ModifiedDate)
.AsQueryable();
query = (IQueryable<T>)thenByMethod.Invoke(null, new object[] { query, orderByExp });
return await query.ToListAsync();
}
尝试运行此查询会导致:
System.InvalidOperationException:未为类型'System.Guid'和'System.Guid'定义二进制运算符GreaterThan。
哦,亲爱的。 Guids像人类一样,不喜欢互相比较。要么是,否则我使用了错误的比较表达式。
一个显而易见的解决方案是将Guid转换为字符串以进行比较,但是(a)效率似乎较低,(b)我不知道如何编写Expression
将Guid转换为字符串。
转换为字符串是否正确?如果是这样,Expression
会做什么?如果没有,正确的方法是什么?
答案 0 :(得分:3)
通常的方法是用Guid.CompareTo(Guid)调用代替不受支持的Guid
运算符,例如代替
guidA > guidB
使用
guidA.CompareTo(guidB) > 0
根据您的情况,替换
var greaterThanMethod = Expression.GreaterThan(propertyExp, target);
使用
var compareTo = Expression.Call(propertyExp, "CompareTo", Type.EmptyTypes, target);
var greaterThanMethod = Expression.GreaterThan(compareTo, Expression.Constant(0));
这适用于大多数查询提供程序(LINQ到对象,LINQ到实体(EF6))。不幸的是,它不适用于需要different approach的EF Core2.x。如果是这种情况,请按照链接的答案中的说明注册自定义GuidFunctions
类,并使用此类:
var greaterThanMethod = Expression.Call(
typeof(GuidFunctions), "IsGreaterThan", Type.EmptyTypes,
propertyExp, target);