【问题标题】:Invoking lambda expressions in Expression trees在表达式树中调用 lambda 表达式
【发布时间】:2014-06-27 16:05:52
【问题描述】:

我有一个 SelectionCriteria 类,用于构建基于 PredicateBuilder 的实体框架查询表达式。在其限制范围内,它运行良好。我想扩展它,以便它可以查询字段是否包含子字符串。我的问题是我看不到如何构建所需的表达式对象。

我的实际班级支持和、或、不,但它们与我的问题无关。所以我简化了我的示例代码,只处理一个二进制操作:

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(),它返回一个表达式> 并应用一个.Contains(

将 Contains() 应用于字符串很简单,给定一个字符串来比较它,但我很难弄清楚如何在表达式中做同样的事情。

想法?

【问题讨论】:

    标签: c# lambda expression-trees linq-expressions


    【解决方案1】:

    像往常一样,我想错了。我不需要调用字符串方法的 lambda,我需要 MethodExpression(此处从 methodMap 字典中提取):

    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 进行实际的“喜欢”比较有点复杂,PatIndex() 返回一个 int,我需要将它包装在 >0 表达式中,但它也可以正常工作.

    【讨论】:

      猜你喜欢
      • 2016-10-05
      • 2012-01-15
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多