带有通用属性的LINQ表达式

3

我的问题与这个问题有关:linq-expression-with-generic-class-properties

这次我想要获取具有相同id的新创建对象。id实际上是外键,因此可以有不同的名称。

我想出了以下方法:

public static IEnumerable<T> GetNew<TId, T>(IQueryable<T> objects, TId id, DateTime date, Expression<Func<T, TId>> idSelector) 
    where T : class, ISyncable<TId>
{
    return objects.Where(o => idSelector(o) == id && o.CreatedDate > date);
}

该方法将会被这样调用:
var modified = GetNew(dbObjects, id, date, entity => entity.PersonId);

很不幸,我遇到了错误:

'idSelector' is a variable but is used like a method.

传递给该方法的表达式应该做到这样:
objects.Where(o => o.PersonId == id && o.CreatedDate > date);
1个回答

9
您需要构建一个新的表达式来表示条件,并将其传递给Where
public static IEnumerable<T> GetNew<TId, T>(IQueryable<T> objects, TId id, DateTime date, Expression<Func<T, TId>> idSelector)
    where T : class, ISyncable<TId>
{
    var paramExpr = Expression.Parameter(typeof(T));
    var idEqExpr = Expression.Equal(Expression.Invoke(idSelector, paramExpr), Expression.Constant(id));
    var createdPropExpr = Expression.Property(paramExpr, "CreatedDate");
    var gtExpr = Expression.GreaterThan(createdPropExpr, Expression.Constant(date));
    var andExpr = Expression.And(idEqExpr, gtExpr);

    var condExpr = Expression.Lambda<Func<T, bool>>(andExpr, paramExpr);

    return objects.Where(condExpr);
}

编辑:如果您知道idSelector是一个属性表达式,您可以提取引用的属性并创建一个新的属性表达式,而不是使用Invoke

var idProperty = (PropertyInfo)((MemberExpression)idSelector.Body).Member;
var idEqExpr = Expression.Equal(Expression.Property(paramExpr, idProperty), Expression.Constant(id));

1
或者调用 Where 方法两次。 - MarcinJuraszek
不幸的是,我遇到了异常:LINQ表达式节点类型“Invoke”在LINQ to Entities中不受支持。难道没有办法防止这种情况发生吗? - niklr
1
@Moo - 如果你期望 idSelector 是一个属性表达式,那么你可以使用它来代替 Invoke。如果你不能这样做,你就必须尝试递归地解构它,并构建一个不使用 Invoke 的新表达式。请参见更新。 - Lee
@Lee,我已经构建了一个类似于你描述的表达式。它对于例如LessThan或GreaterThan表达式非常有效。但是,如果我将两个表达式相加,它会使用'&'符号,我需要它是字符串“AND”。你能告诉我如何解决这个问题吗?谢谢。 - Jones
@Jones - 你能否创建一个带有示例的新问题?听起来你正在尝试将表达式转换为查询字符串,这种情况下你可以创建一个ExpressionVisitor来遍历表达式并将每个节点转换为字符串表示形式。 - Lee
@李 你好李,我发现我必须使用.AndAlso,然后我才能得到正确的行为: // 使用AndAlso => 'AND'操作符组合表达式。不要使用And => '&' queryExpression = Expression.AndAlso(smallerExp, largerExp); 谢谢! - Jones

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接