【问题标题】:Converting selector expression to predicate expression将选择器表达式转换为谓词表达式
【发布时间】:2014-09-24 13:52:25
【问题描述】:

我需要一种将选择器表达式转换为谓词表达式的方法,以便将它与一些参数一起传递给查询提供程序。

private Expression<Func<T, TKey>> PrimaryKeySelector { get; set; }

private ISpecification<T> GetByPrimaryKeySpecification(TKey key) {
   lambda = ... // some magic, also unicorns
   return new ExpressionSpecification(lambda);
}

在第一次尝试中,我使用Expression.Equal 构建了谓词表达式,但这会导致分析表达式的查询提供程序出现问题(例如,NHibernate 由于未知的Equal 语句而失败)。另外,我已经看到了创建新的ParameterExpressions 并通过调用它来使用 selector 表达式 的结果的解决方案,但是我觉得这很糟糕,因为 1)性能和 2)参数表达式已包含在 选择器表达式中

有什么建议吗?

【问题讨论】:

    标签: c# linq nhibernate lambda expression


    【解决方案1】:

    您在这里要做的是将一个表达式与另一个表达式组合在一起。用函数来做这件事很容易;用表达式来做这件事需要更多的工作。

    你可以做的是将参数的所有实例替换为你的第二个表达式,谓词,用你的第一个函数的主体,选择器。

    public static Expression<Func<TFirstParam, TResult>>
        Compose<TFirstParam, TIntermediate, TResult>(
        this Expression<Func<TFirstParam, TIntermediate>> first,
        Expression<Func<TIntermediate, TResult>> second)
    {
        var param = Expression.Parameter(typeof(TFirstParam), "param");
    
        var newFirst = first.Body.Replace(first.Parameters[0], param);
        var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
    
        return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
    }
    

    这依赖于以下方法将一个表达式的所有实例替换为另一个:

    public static Expression Replace(this Expression expression,
        Expression searchEx, Expression replaceEx)
    {
        return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
    }
    internal class ReplaceVisitor : ExpressionVisitor
    {
        private readonly Expression from, to;
        public ReplaceVisitor(Expression from, Expression to)
        {
            this.from = from;
            this.to = to;
        }
        public override Expression Visit(Expression node)
        {
            return node == from ? to : base.Visit(node);
        }
    }
    

    最后一个缺失的难题是您将无法使用== 运算符在通用方法中比较您的值,因为您无法确定该类型是否覆盖了== 运算符;您需要手动构建该等式表达式:

    public static Expression<Func<T, bool>> EqualsValue<T>(T value)
    {
        var param = Expression.Parameter(typeof(T));
        var body = Expression.Equal(param, Expression.Constant(value));
        return Expression.Lambda<Func<T, bool>>(body, param);
    }
    

    这允许你写:

    return PrimaryKeySelector.Compose(EqualsValue(key));
    

    至于您对性能的担忧,请不要担心。您构建这样一个表达式树所花费的时间将比您使用它们发出的网络请求慢几个数量级。在表达式操作上花费一点额外的时间甚至不会对应用程序的性能产生可衡量的影响;它将远低于网络请求的噪音

    【讨论】:

    • Mh。由于错误“无法将运算符 == 应用于“TKey”和“TKey”类型的操作数,这在某种程度上不起作用。在这里查看我当前的实现:pastebin.com/iEt5ynNb
    • @xvdiff 如果您不在可以知道键类型的上下文中,那么您需要手动构建该表达式,而不是使用 lambda,请参阅编辑。
    • 但随后 NHibernate 实现了繁荣。 -> System.NotSupportedException: Boolean Equals(System.Object)
    • @xvdiff Expression.Equal 方法表示== 运算符,而不是对Equals 方法的调用。你必须在你的表达式中的其他地方调用Equals
    • 有趣。我找不到给 Equals 的电话。我有 KeySelector、Spec,我所做的就是从 NHibernate 请求 .Query,然后将 Spec 应用到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 1970-01-01
    相关资源
    最近更新 更多