在表达式树中调用lambda表达式 [英] Invoking lambda expressions in Expression trees

查看:117
本文介绍了在表达式树中调用lambda表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用于基于PredicateBuilder构建Entity Framework查询表达式的SelectionCriteria类.在其范围内,它工作正常.我想扩展它,以便它可以查询字段是否包含子字符串.我的问题是我看不到如何构建所需的表达式对象.

I have a SelectionCriteria class that I use for building Entity Framework query expressions, based on PredicateBuilder. Within its limits, it's working fine. I'd like to extend it so that it can query whether a field contains a substring. My problem is that I can't see how to build the needed expression object.

我的实际班级支持和(或)和(或)不支持,但它们与我的问题无关.因此,我简化了示例代码,使其仅处理单个二进制操作:

My actual class supports and, or, and not, but they aren't relevant to my question. So I've simplified my example code to handle only a single binary operation:

public class SelectionCriteria
{
    public SelectionComparison selectionComparison { get; set; }
    public string fieldName { get; set; }
    public object fieldValue { get; set; }

    public Expression<Func<T, bool>> constructSinglePredicate<T>()
    {
        var type = typeof(T);

        if (type.GetProperty(this.fieldName) == null && type.GetField(this.fieldName) == null)
            throw new MissingMemberException(type.Name, this.fieldName);

        ExpressionType operation;
        if (!operationMap.TryGetValue(this.selectionComparison, out operation))
            throw new ArgumentOutOfRangeException("selectionComparison", this.selectionComparison, "Invalid filter operation");

        var parameter = Expression.Parameter(type);
        var member = Expression.PropertyOrField(parameter, this.fieldName);
        var value = (this.fieldValue == null) ? Expression.Constant(null) : Expression.Constant(this.fieldValue, this.fieldValue.GetType());

        try
        {
            var converted = (value.Type != member.Type)
                ? (Expression) Expression.Convert(value, member.Type)
                : (Expression) value;

            var comparison = Expression.MakeBinary(operation, member, converted);

            var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);

            return lambda;
        }
        catch (Exception)
        {
            throw new InvalidOperationException(
                String.Format("Cannot convert value \"{0}\" of type \"{1}\" to field \"{2}\" of type \"{3}\"", this.fieldValue,
                    value.Type, this.fieldName, member.Type));
        }
    }

    private static Dictionary<SelectionComparison, ExpressionType> operationMap =
        new Dictionary<SelectionComparison, ExpressionType>
        {
            { SelectionComparison.Equal, ExpressionType.Equal },
            { SelectionComparison.GreaterThan, ExpressionType.GreaterThan },
        };
}

public enum SelectionComparison
{
    Equal,
    GreaterThan,
    Contains,
};

用法很简单:

var criteria = new SelectionCriteria
{
    selectionComparison = SelectionComparison.GreaterThan,
    fieldName = "worktobegindate",
    fieldValue = DateTime.Now.AddDays(-2)
};

var predicate = criteria .constructPredicate<job>();
var jobs = myDbContext.jobs.Where(predicate);

所以,我的问题-我需要一个方法,例如上面的ConstructSinglePredictate(),该方法返回一个Expression>并应用.Contains(

将Contains()应用于字符串(给定要与之进行比较的字符串)很简单,但是我很难弄清楚如何在表达式中执行相同的操作.

So, my problem - I need a method, like my constructSinglePredictate() above, that returns an Expression> and applies a .Contains(

It's trivial to apply Contains() to a string, given a string to compare it to, but I'm having difficulty figuring out how to do the same in an Expression.

想法?

推荐答案

像往常一样,我在错误地考虑事情.我不需要一个在字符串上调用方法的lambda,我需要一个MethodExpression(这里是从methodMap字典中提取的):

As is usual, I was thinking about things incorrectly. I don't need a lambda that calls a method on a string, I need a MethodExpression (here extracted from the methodMap dictionary):

public Expression<Func<T, bool>> constructMethodCallPredicate<T>()
{
    var type = typeof(T);

    if (type.GetProperty(this.fieldName) == null && type.GetField(this.fieldName) == null)
        throw new MissingMemberException(type.Name, this.fieldName);

    MethodInfo method;
    if (!methodMap.TryGetValue(this.selectionComparison, out method))
        throw new ArgumentOutOfRangeException("selectionComparison", this.selectionComparison, "Invalid filter operation");

    var parameter = Expression.Parameter(type);
    var member = Expression.PropertyOrField(parameter, this.fieldName);
    var value = (this.fieldValue == null) ? Expression.Constant(null) : Expression.Constant(this.fieldValue, this.fieldValue.GetType());

    try
    {
        var converted = (value.Type != member.Type)
            ? (Expression)Expression.Convert(value, member.Type)
            : (Expression)value;

        var methodExpression = Expression.Call(member, method, converted);

        var lambda = Expression.Lambda<Func<T, bool>>(methodExpression, parameter);

        return lambda;
    }
    catch (Exception)
    {
        throw new InvalidOperationException(
            String.Format("Cannot convert value \"{0}\" of type \"{1}\" to field \"{2}\" of type \"{3}\"", this.fieldValue,
                value.Type, this.fieldName, member.Type));
    }
}

private static readonly Dictionary<SelectionComparison, MethodInfo> methodMap =
    new Dictionary<SelectionComparison, MethodInfo>
    {
        { SelectionComparison.Contains, typeof(string).GetMethod("Contains", new[] { typeof(string) }) },
        { SelectionComparison.StartsWith, typeof(string).GetMethod("StartsWith", new[] { typeof(string) }) },
        { SelectionComparison.EndsWith, typeof(string).GetMethod("EndsWith", new[] { typeof(string) }) },
    };

public enum SelectionComparison
{
    Equal,
    NotEqual,
    LessThan,
    LessThanOrEqual,
    GreaterThan,
    GreaterThanOrEqual,
    Contains,
    StartsWith,
    EndsWith,
};

使用SqlFunctions.PatIndex进行实际的"Like"比较工作要复杂一些,PatIndex()返回一个int,我需要将其包装在一个> 0表达式中,但它也可以正常工作

Getting an actual "Like" comparison to work, using SqlFunctions.PatIndex is a bit more complicated, PatIndex() returns an int, and I need to wrap it in a >0 expression, but it works just fine, as well.

这篇关于在表达式树中调用lambda表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆