C#表达式以使用Guid书签

时间:2019-03-26 11:21:02

标签: c# linq linq-expressions

我有一个需要处理几个需要同步/备份的表的要求。所有这些表的类都实现ITrackModifiedDate

interface ITrackModifiedDate
{
    DateTime ModifiedDate { get; set; }
}

我需要分批处理这些内容,这意味着我需要为上次到达的位置添加书签。因此,我可以按ModifiedDate进行排序,并只跟踪最后一个ModifiedDate。但是该计划中存在一个缺陷:多个记录具有相同的ModifiedDate很容易发生。这意味着我需要一个辅助标识符,而我通常可以唯一依赖的是关键字段,它始终是Guid

我从hereExpression那里得到了一些帮助,但是当我尝试修改以添加“大于”子句时,事情就解决了。

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会做什么?如果没有,正确的方法是什么?

1 个答案:

答案 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);