使用 Guid 书签的 C# 表达式
C# Expression to use Guid bookmark
我需要处理几个需要 synchronized/backed 的表格。所有这些表的 类 实现 ITrackModifiedDate
:
interface ITrackModifiedDate
{
DateTime ModifiedDate { get; set; }
}
我需要分批处理这些,这意味着我需要在我上次到达的地方添加书签。所以我可以按 ModifiedDate
排序,只跟踪我的最后一个 ModifiedDate
。但是计划中有一个问题:很容易发生多个记录具有相同 ModifiedDate
的情况。这意味着我需要一个辅助标识符,而我唯一可以依赖的就是关键字段,它总是 Guid
.
我从 那里得到了一些关于 Expression
内容的帮助,但是当我尝试修改 "greater than" 子句时,事情发生了变化。
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: The binary operator GreaterThan is not defined for the types 'System.Guid' and 'System.Guid'.
哦,亲爱的。 Guids 似乎和人类一样,不喜欢被比较。要么,要么我使用了错误的比较表达式。
想到的明显解决方案是将 Guid 转换为字符串以进行比较,但是 (a) 这似乎有点低效,并且 (b) 我不知道如何编写 Expression
将 Guid 转换为字符串。
转换为字符串是否正确?如果是这样,什么 Expression
可以完成这项工作?如果不是,正确的做法是什么?
一般方法是用 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 to Objects、LINQ To Entities (EF6))。不幸的是不适用于需要 的 EF Core 2.x。如果是这种情况,请按照链接答案中的说明注册自定义 GuidFunctions
class,然后改用它:
var greaterThanMethod = Expression.Call(
typeof(GuidFunctions), "IsGreaterThan", Type.EmptyTypes,
propertyExp, target);
我需要处理几个需要 synchronized/backed 的表格。所有这些表的 类 实现 ITrackModifiedDate
:
interface ITrackModifiedDate
{
DateTime ModifiedDate { get; set; }
}
我需要分批处理这些,这意味着我需要在我上次到达的地方添加书签。所以我可以按 ModifiedDate
排序,只跟踪我的最后一个 ModifiedDate
。但是计划中有一个问题:很容易发生多个记录具有相同 ModifiedDate
的情况。这意味着我需要一个辅助标识符,而我唯一可以依赖的就是关键字段,它总是 Guid
.
我从 Expression
内容的帮助,但是当我尝试修改 "greater than" 子句时,事情发生了变化。
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: The binary operator GreaterThan is not defined for the types 'System.Guid' and 'System.Guid'.
哦,亲爱的。 Guids 似乎和人类一样,不喜欢被比较。要么,要么我使用了错误的比较表达式。
想到的明显解决方案是将 Guid 转换为字符串以进行比较,但是 (a) 这似乎有点低效,并且 (b) 我不知道如何编写 Expression
将 Guid 转换为字符串。
转换为字符串是否正确?如果是这样,什么 Expression
可以完成这项工作?如果不是,正确的做法是什么?
一般方法是用 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 to Objects、LINQ To Entities (EF6))。不幸的是不适用于需要 GuidFunctions
class,然后改用它:
var greaterThanMethod = Expression.Call(
typeof(GuidFunctions), "IsGreaterThan", Type.EmptyTypes,
propertyExp, target);