繁体   English   中英

转换表达式 <Func<TEntity, IEnumerable<TProperty> &gt;&gt; valueSelector,将TValue []的值传递给表达式 <Func<TElement, bool> &gt;

[英]Converting Expression<Func<TEntity, IEnumerable<TProperty>>> valueSelector, TValue[] values to Expression<Func<TElement, bool>>

我正在尝试编写一个已解析的文本到EF查询表达式库。 想象一下一个文本框,用户在其中输入以逗号分隔的整数列表。 我将拆分并解析它,并用它构成一个int数组。 但后来我需要创建一个表达式为它我的一个要求,就是要把ctx => ctx.User.Receipts.Select(x => x.ReceiptID)和收据的ID(整数)的列表,并产生一个Expression ,做像这样的东西:

ctx.User.Where(x => x.Receipts.Any(y => listOfIds.Contains(y.ReceiptId));

我认为它的签名应该是这样的:

Expression<Func<TEntity, bool>> CreateListExpression(Expression<Func<TEntity, IEnumerable<TProperty>>> accessor, TProperty[] constantValues)

样本用法如下所示:

ctx => ctx.User.Where(CreateListExpression(x => x.Receipts.Select(y => y.ReceiptId), new int[] { 1, 2, 3, 4, 5 });

...但是我很灵活,只有1个约束。 在SQL中,我正在寻找与此类似的东西(就我检索数据的效率以及确保工作在SQL端而言)而言:

select u.* 
from Users u 
join Receipts on r.UserID = u.UserID 
where r.ReceiptID in (1, 2, 3, 4, 5)

有什么特殊的原因为什么需要动态生成这些表达式,而不是仅在DAL中为每个潜在查询提供一个函数?

您的描述听起来像是用户只是为查询提供输入值,而不是查询本身的整体结构,在这种情况下,我不会使事情复杂化。 您的模型还可以传递IQueryable并根据用户的选择(添加任意数量的约束,订单内容等)根据需要进行修改,而不是从头开始创建表达式。

如果您确实需要采用动态表达方式,您可以指定更多问题吗? 我不确定那是什么实际问题。 您当然可以自己构建表达式。 恕我直言,这真是一个痛苦的世界,用手创造非平凡的表情并不是特别有趣。 因此,如果有另一种方法,我通常建议选择另一种方法。

我也不确定您打算如何动态定义这些属性,但是您想出的方法似乎很难。 我只会诉诸一种好的旧的占位技术。 尝试使用诸如common parameterReplacer之类的方法:

public class ParameterReplacer : ExpressionVisitor
{
    private readonly ParameterExpression m_parameter;
    private readonly Expression m_replacement;
    public ParameterReplacer(ParameterExpression parameter, Expression replacement)
    {
        this.m_parameter = parameter;
        this.m_replacement = replacement;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (object.ReferenceEquals(node, m_parameter))
            return m_replacement;
        return node;
    }

}

然后定义一个扩展方法,该方法应该替换表达式中的参数。

public static class ExpressionExtensions
{
    public static Expression<Func<T1, TResult>> FixParam<T1, T2, TResult>(this Expression<Func<T1, T2, TResult>> expression, T2 parameterValue)
    {
        var parameterToRemove = expression.Parameters.ElementAt(1);
        var replacer = new ParameterReplacer(parameterToRemove, Expression.Constant(parameterValue, typeof(T2)));
        return Expression.Lambda<Func<T1, TResult>>(replacer.Visit(expression.Body), expression.Parameters.Where(p => p != parameterToRemove));
    }
}

这是第二个参数,但是您当然可以轻松更改它。 现在,我将直接使用其他占位符来定义最终谓词,例如:

Expression<Func<User, int[], bool>> queryDefinition = (user, receiptIds) => user.Receipts.Any(r => receiptIds.Contains(r.Id));

然后在有值时替换第二个参数

var ids = new []{3, 6};
var finalquery = queryDefinition.FixParam(ids);

这为您提供了不必担心任何表达式树构建的优点,并且您可以完全控制代码中的谓词。 PS: https : //dotnetfiddle.net/Widget/T3oj5U

实际上,一旦您意识到

(Parent p) => p.Children.Any(c => filter(c.Property))

相当于

(Parent p) => p.Children.Select(c => c.Property).Any(v => filter(v))

所以功能可能是这样的

Expression<Func<TEntity, bool>> CreateListExpression<TEntity, TProperty>(Expression<Func<TEntity, IEnumerable<TProperty>>> accessor, TProperty[] constantValues)
{
    Expression<Func<TProperty, bool>> predicate = v => constantValues.Contains(v);
    var body = Expression.Call(typeof(Enumerable), "Any", 
        new[] { typeof(TProperty) }, accessor.Body, predicate);
    return Expression.Lambda<Func<TEntity, bool>>(body, accessor.Parameters);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM