如何将Func传递给一个基于该Func封装Expression的类?

时间:2017-02-17 19:16:30

标签: c# lambda linq-to-sql linq-to-entities linq-expressions

我的课程DropdownFilter有:

private readonly Func<TEntity, string> fieldWhichMustEqualValue;

public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
    return filteredEntityCollection.Where(entity => this.fieldWhichMustEqualValue(entity) == value);
}

我用它作为:

IQueryable<Invoice> entityCollectionToFilterAndOrder = ...

var dropdownFilter = new DropdownFilter<Invoice>(invoice => invoice.SomeProperty);
entityCollectionToFilterAndOrder = dropdownFilter.Filter(entityCollectionToFilterAndOrder, "bla bla bla");

给了我

  

LINQ表达式节点类型&#39;调用&#39; LINQ不支持   实体。

我理解问题是我基本上要求等同于Invoke的SQL,这当然是错误的。

我应该如何重写代码?我明白它需要是一种表达方式。我的目标是DropDownFilter的使用者只需指定TEntity的属性,而不提供表达式。即表达式必须封装在过滤器中。

我试过了:

Expression<Func<TEntity, string>> expr = mc => this.fieldWhichMustEqualValue(mc);
Expression le = Expression.Equal(expr.Body, Expression.Constant(value));
var lambda = Expression.Lambda<Func<TEntity, bool>>(le, expr.Parameters);
return filteredEntityCollection.Where(lambda);

但它基本上给了我相同的结果。

1 个答案:

答案 0 :(得分:0)

Func<TEntity, string>是一个C#函数,因此无法在SQL服务器中执行。但是,如果将其更改为表达式,则可以起作用:

private readonly Expression<Func<TEntity, string>> fieldWhichMustEqualValue;

但是,现在Filter函数无法编译,因为您无法调用表达式。你需要做的是撰写一个新的表达式:

public static UnaryExpression WrapInPropertyAccess(this ConstantExpression constant) {
    Tuple<object> container = new Tuple<object>(constant.Value);
    Expression containerExpression = Expression.Constant(container, typeof(Tuple<object>));
    MemberExpression propAccess = Expression.Property(containerExpression, "Item1");
    UnaryExpression result = Expression.Convert(propAccess, constant.Type); // Cast back the object to the value type
    return result;
}

private Expression<TEntity, bool> FieldEqualsValue(string value) {
    var comparison = Expression.Equal(fieldWhichMustEqualValue.Body, Expression.Constant(value).WrapInPropertyAccess());
    return Expression.Lambda<Func<TEntity, bool>>(comparison, fieldWhichMustEqualValue.Parameters);
}

public override IQueryable<TEntity> Filter(IQueryable<TEntity> filteredEntityCollection, string value)
{
    var condition = FieldEqualsValue(value);
    return filteredEntityCollection.Where(condition);
}

WrapInPropertyAccess函数不是严格需要的,但使用它会使Entity Framework生成参数化表达式。