使用 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);