实体框架 - 动态查询

时间:2013-09-01 12:20:24

标签: entity-framework expression

我正在创建一个动态表达式构建器并尝试实现' like'功能。在写我自己之前,我已经搜索了任何现有的功能,并找到了一个接近我需要的功能。经过几次实验后,我无法让它为字符串以外的类型运行。

当我传递int类型的参数时,我收到此错误:

  

Method' System.String ToString()'声明类型' System.String'不能使用类型&System; System.Int32'

的实例调用

我的代码如下所示:

private static MethodCallExpression GetLowerCasePropertyAccess(MemberExpression propertyAccess)
{
    //return Expression.Call(Expression.Call(propertyAccess, "ToString", new Type[0]), typeof(string).GetMethod("ToLower", new Type[0]));
    return Expression.Call(Expression.Call(propertyAccess, typeof(string).GetMethod("ToString", System.Type.EmptyTypes)), typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
}

private static readonly MethodInfo ContainsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) });

public static Expression<Func<T, bool>> Create<T>(string propertyName, ComparisonOperators comparisonOperator, dynamic comparedValue1, dynamic comparedValue2 = null)
{
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x");
    MemberExpression memberExpression = Expression.MakeMemberAccess(parameterExpression, typeof(T).GetProperty(propertyName));
    ConstantExpression constantExpression = Expression.Constant(comparedValue1, comparedValue1.GetType());
    Expression expressionBody = null;

    switch (comparisonOperator)
    {
        ...

        case ComparisonOperators.Contains:
            //var indexOf = Expression.Call(memberExpression, "IndexOf", null, Expression.Constant(comparedValue1, typeof(string)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase));
            //expressionBody = Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0));
            expressionBody = Expression.Call(GetLowerCasePropertyAccess(memberExpression), ContainsMethod, Expression.Constant(comparedValue1.ToLower()));
            break;
    }

    return Expression.Lambda<Func<T, bool>>(expressionBody, new ParameterExpression[] { parameterExpression });
}

3 个答案:

答案 0 :(得分:0)

我不确定我完全明白你在做什么,但我认为你的错误是由这一行造成的:

return Expression.Call(Expression.Call(propertyAccess, typeof(string).GetMethod("ToString", System.Type.EmptyTypes)), typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));

总是会尝试为ToString类型调用string方法,因此如果您尝试使用Int32属性,则会尝试调用String.ToString()因为ToString()的实现对于不同的类型会有所不同,并且这两个实现不一定兼容,所以你会得到你看到的异常:

Method 'System.String ToString()' declared on type 'System.String' cannot be called with instance of type 'System.Int32'

从你看起来的样子来看,我认为这可能就是你所追求的:

return Expression.Call(Expression.Call(propertyAccess, propertyAccess.Type.GetMethod("ToString", System.Type.EmptyTypes)), typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));

哪个会使用ToString的正确实现(从propertyAccess.Type获取的类型)。

答案 1 :(得分:0)

Linq to entities不支持.ToString方法。要将数值转换为字符串,您需要使用SqlFunctions.StringConvert方法。我已修复您的代码,现在您可以对字符串和数字列执行like

private static Expression GetConvertToStringExpression(Expression e)
{
    // if property string - no cast needed
    // else - use SqlFunction.StringConvert(double?) or SqlFunction.StringConvert(decimal?);
    Expression strExpression = null;
    if (e.Type == typeof(string))
        strExpression = e;

    var systemType = Nullable.GetUnderlyingType(e.Type) ?? e.Type;

    if (systemType == typeof(int) 
        || systemType == typeof(long) 
        || systemType == typeof(double)
        || systemType == typeof(short)
        || systemType == typeof(byte)) // continue
    {
        // cast int to double
        var doubleExpr = Expression.Convert(e, typeof (double?));
        strExpression = Expression.Call(StringConvertMethodDouble, doubleExpr);
    }

    if (systemType == typeof (decimal))
    {
        // call decimal version of StringConvert method
        // cast to nullable decimal
        var decimalExpr = Expression.Convert(e, typeof (decimal?));
        strExpression = Expression.Call(StringConvertMethodDecimal, decimalExpr);
    }

    return strExpression;
}

private static MethodCallExpression GetLowerCasePropertyAccess(Expression propertyAccess)
{
    var stringExpression = GetConvertToStringExpression(propertyAccess);
    if (stringExpression == null)
        throw new Exception(string.Format("Not supported property type {0}", propertyAccess.Type));

    return Expression.Call(stringExpression,
        typeof (string).GetMethod("ToLower", Type.EmptyTypes));
}

private static readonly MethodInfo StringConvertMethodDouble = typeof (SqlFunctions).GetMethod("StringConvert",
    new Type[] {typeof (double?)});
private static readonly MethodInfo StringConvertMethodDecimal = typeof(SqlFunctions).GetMethod("StringConvert",
    new Type[] { typeof(decimal?) });

答案 2 :(得分:0)

我刚刚做了类似的事情:

    public Expression<Func<T,bool>> BuildContainsExpression<T>(MemberExpression memberExp, object comparedValue)
    {
        var parameter = Expression.Parameter(memberExp.Member.DeclaringType, "x");
        var method = typeof(string).GetMethod("Contains", types: new[] { typeof(string) });

        var comparison = Expression.Equal(
                    Expression.Call(
                        method: method,
                        instance: memberExp,
                        arguments: Expression.Constant(comparedValue)),
                    Expression.Constant(true)
            );

        return Expression.Lambda<Func<T, bool>>(comparison, parameter);
    }

它会构建如下表达式: x.Language.Contains("tr") (带有我的动态参数) Sample Expression