表达式.类似于C#

本文关键字:类似于 表达式 | 更新日期: 2023-09-27 18:10:16

例如:x=>x。Name="g">

我有这样的代码块

public Expression<Func<TEntity, bool>> SearchExpression()
{
    var c = new ConstantExpression[_paramList.Count];
    var b = new BinaryExpression[_paramList.Count];
    BinaryExpression comparisonExpression = null;
    var entity = Expression.Parameter(typeof(TEntity));
    for (int i = 0; i < _paramList.Count; i++)
    {
        var value = Convert.ChangeType(_paramList[i].Item2 /*"g"*/, _paramList[i].Item3 /*System.String*/);
        c[i] = Expression.Constant(value); //"g"
        // PROBLEM IS HERE
        b[i] = Expression.Equal(Expression.Property(entity, _paramList[i].Item1 /*Name*/, c[i]);
        // PROBLEM IS HERE

    }
    _paramList.Clear();
    comparisonExpression = b.Aggregate(Expression.And);
    return Expression.Lambda<Func<TEntity, bool>>(comparisonExpression, entity);
}

工作起来很有魅力,但我需要表达式。喜欢(喜欢"g"不等于"g"(

Expression.Like(Expression.Property(entity, _paramList[i].Item1), c[i])

但是C#表达式树不支持Like方法

更新

我写了这样的东西:

Expression.Call(Expression.Property(entity, _paramList[i].Item1),
                typeof(String).GetMethod("Contains"), new Expression[] { c[i] });  

但是我需要BinaryExpression而不是MethodCallExpression

表达式.类似于C#

您可以通过在方法调用上添加equals表达式来使代码正常工作,如下所示:

    b[i] = Expression.Equal(
        Expression.Call(Expression.Property(entity, _paramList[i].Item1),
        typeof (String).GetMethod("Contains"), 
          new Expression[] {c[i]}), Expression.Constant(true));

在伪代码中,它读作:

b[i] = entity => entity.someProperty.Contains(c[i]) == true;

它将为您返回一个二进制表达式。

这个答案不考虑您的数组和"and"聚合,但这应该被视为一个单独的问题。

考虑这个类别:

class MyEntity { string Name { get; set; } }

我们要查询:

select ... from MyEntity where Name like '%query%';

以下方法是上述查询模式的一般实现:

static Expression<Func<TEntity, bool>> Like<TEntity>(string propertyName, string queryText)
{
    var parameter = Expression.Parameter(typeof (TEntity), "entity");
    var getter = Expression.Property(parameter, propertyName);
    //ToString is not supported in Linq-To-Entities, throw an exception if the property is not a string.
    if (getter.Type != typeof (string))
        throw new ArgumentException("Property must be a string");
    //string.Contains with string parameter.
    var stringContainsMethod = typeof (string).GetMethod("Contains", new[] {typeof (string)});
    var containsCall = Expression.Call(getter, stringContainsMethod,
        Expression.Constant(queryText, typeof (string)));
    return Expression.Lambda<Func<TEntity, bool>>(containsCall, parameter);
}

如果想要query%%query的模式,可以使用string.StartsWithstring.EndsWith而不是Contains

此外,如果调整签名,则可以在多个调用之间共享参数。

如果属性的数据类型不是字符串,则当前实现将引发异常。看看这个答案https://stackoverflow.com/a/3292773/668272用于将数字转换为字符串。

我用自己编写的脚本语言完成了这项工作,它允许您说出类似name like 'bob%'的内容。诀窍是,您需要将它映射到一个方法调用,该方法调用接受值和正则表达式,并从Expression中调用它。

如果你看一下我的Wire脚本语言中的LikeEvaluator类,你会发现我是如何做到的:

static class LikeEvaluator
{
    private static readonly MethodInfo ApplyLikeMethodInfo=typeof(LikeEvaluator).GetMethod("ApplyLike");
    private static readonly MethodInfo ApplyLikeNoCaseMethodInfo=typeof(LikeEvaluator).GetMethod("ApplyLikeNoCase");
    public static Expression Like(CaseMode caseMode, Expression lhs, Expression pattern)
    {
        Expression x=null;
        if(caseMode==CaseMode.Sensitive)
        {
            x=Expression.Call(ApplyLikeMethodInfo,lhs,pattern);
        }
        else
        {
            x=Expression.Call(ApplyLikeNoCaseMethodInfo,lhs,pattern);
        }
        return x;
    }
    public static bool ApplyLike(string text, string likePattern)
    {
        string pattern=PatternToRegex(likePattern);
        return Regex.IsMatch(text,pattern,RegexOptions.None);
    }
    public static bool ApplyLikeNoCase(string text, string likePattern)
    {
        string pattern=PatternToRegex(likePattern);
        return Regex.IsMatch(text,pattern,RegexOptions.IgnoreCase);
    }
    public static string PatternToRegex(string pattern)
    {
        pattern=Regex.Escape(pattern);
        pattern=pattern.Replace("%",@".*");
        pattern=string.Format("^{0}$",pattern);
        return pattern;
    }
}