-->

处理在LINQ表达式调用空裁判例外(Handle null ref exceptions in LI

2019-11-04 15:26发布

我试图创建一个通用的表达式生成器,很好,只要其基本工作原理没有对象的值是零。

我当前的代码看起来像这样(StartsWith为例):

                case FilterOperationTypes.StartsWith:
                {
                    ParameterExpression e = Expression.Parameter(typeof(T), "e");
                    PropertyInfo propertyInfo = typeof(T).GetProperty(field);
                    MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
                    ConstantExpression c = Expression.Constant(val, typeof(string));
                    MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
                    Expression call = Expression.Call(m, mi, c);                        
                    return Expression.Lambda<Func<T, bool>>(call, e);
                }

假设场是物业客户名称。 据我所知,最终的实际表现会像:

e.CustomerName.StartsWith(val)

如果客户名称为空,将,当然,无法调用StartsWith方法,这是非常清楚。

我试图做这样的事情:

                case FilterOperationTypes.StartsWith:
                {
                    ParameterExpression e = Expression.Parameter(typeof(T), "e");
                    PropertyInfo propertyInfo = typeof(T).GetProperty(field);
                    MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
                    ConstantExpression c = Expression.Constant(val, typeof(string));
                    MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
                    Expression call = Expression.IfThenElse(
                        Expression.Equal(m, Expression.Constant(null)),
                        Expression.Constant(null),
                        Expression.Call(m, mi, c));
                    //Expression.Call(m, mi, c);                        
                    return Expression.Lambda<Func<T, bool>>(call, e);
                }

但是,这会产生类型的表达“System.Void”不能被用于返回类型“System.Boolean”异常。

我有点失去截至目前。 也许你们可以把我在正确的方向。

非常感谢!

Answer 1:

您正在寻找Expression.Condition ,而不是IfThenElse ,它代表了条件运算符,而不是if / else语句。 条件运算符解析为一个值,因为它是一个表达式,而不是一个声明。



Answer 2:

得到它现在的工作。 非常感谢Servy推我在正确的方向。

该Expression.Condition正是我需要什么来实现我的目标。 由于实际的对象和对象的属性可以在运行时为空,我不得不窝两个条件:

            PropertyInfo propertyInfo = typeof(T).GetProperty(fieldName);
        MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
        ConstantExpression c = Expression.Constant(comparisonValue, typeof(string));            
        MethodInfo mi = typeof(string).GetMethod(methodName, new Type[] { typeof(string) });

        Expression call = Expression.Condition(Expression.Equal(e, Expression.Constant(null)),
        Expression.NotEqual(e, Expression.Constant(null)),
        Expression.Condition(Expression.Equal(Expression.Property(e, fieldName), Expression.Constant(null)),
        Expression.NotEqual(e, Expression.Constant(null)), Expression.Call(m, mi, c)));
        return Expression.Lambda<Func<T, bool>>(call, e);

在一个空值的情况下,我决定使用

Expression.NotEqual(e, Expression.Constant(null))

就一定要接受“假”。 我敢肯定有一个更好的办法做到这一点,但我不认为一个人的,但。

最终的表达看起来像这样

{e => IIF((e == null), (e != null), IIF((e.CustomerName== null), (e != null), e.CustomerName.StartsWith("547")))}

截至目前,我与解决方案很高兴,但我会尽力去优化它。

我目前的代码实现和使用看起来像这样。 也许它可以帮助你的人之一:

        Expression<Func<T, bool>> GetDetailEx<T>(string fieldName, object comparisonValue, string filterType)
    {
        var e = Expression.Parameter(typeof(T),"e");
        switch (GetFilterOperationType(filterType))
        {
            case FilterOperationTypes.Between:
                //Between is automatically translated in >= AND <= within the ExecuteDeepFilter-Method
                break;
            case FilterOperationTypes.GreaterThanOrEquals:
                {
                    return GenerateConditionalExpression<T>(fieldName, comparisonValue, Expression.GreaterThanOrEqual(
                            Expression.Convert(Expression.Property(e, fieldName), Expression.Constant(comparisonValue).Type),
                            Expression.Constant(comparisonValue)),e);
                }
            case FilterOperationTypes.LessThanOrEquals:
                {
                    return GenerateConditionalExpression<T>(fieldName, comparisonValue, Expression.LessThanOrEqual(
                        Expression.Convert(Expression.Property(e, fieldName), Expression.Constant(comparisonValue).Type),
                        Expression.Constant(comparisonValue)),e);
                }
            case FilterOperationTypes.GreaterThan:
                {
                    return GenerateConditionalExpression<T>(fieldName, comparisonValue, Expression.GreaterThan(
                        Expression.Convert(Expression.Property(e, fieldName), Expression.Constant(comparisonValue).Type),
                        Expression.Constant(comparisonValue)), e);
                }
            case FilterOperationTypes.LessThan:
                {
                    return GenerateConditionalExpression<T>(fieldName, comparisonValue, Expression.LessThan(
                        Expression.Convert(Expression.Property(e, fieldName), Expression.Constant(comparisonValue).Type),
                        Expression.Constant(comparisonValue)), e);
                }
            case FilterOperationTypes.NotEqual:
                {
                    return GenerateConditionalExpression<T>(fieldName, comparisonValue, Expression.NotEqual(
                        Expression.Convert(Expression.Property(e, fieldName), Expression.Constant(comparisonValue).Type),
                        Expression.Constant(comparisonValue)), e);
                }
            case FilterOperationTypes.Equal:
                {
                    return GenerateConditionalExpression<T>(fieldName, comparisonValue, Expression.Equal(
                        Expression.Convert(Expression.Property(e, fieldName), Expression.Constant(comparisonValue).Type),
                        Expression.Constant(comparisonValue)), e);         
                }
            case FilterOperationTypes.EndsWith:
                {
                    return GenerateGenericCallExpression<T>(fieldName, comparisonValue, "EndsWith",e);
                }
            case FilterOperationTypes.StartsWith:
                {
                    return GenerateGenericCallExpression<T>(fieldName, comparisonValue, "StartsWith",e);
                }
            case FilterOperationTypes.NotContains:
                {
                    return GenerateGenericCallExpression<T>(fieldName, comparisonValue, "Contains",e,false); ;
                }
            case FilterOperationTypes.Contains:
                {
                    return GenerateGenericCallExpression<T>(fieldName, comparisonValue, "Contains",e);
                }
            default:
                break;
        }
        return GenerateConditionalExpression<T>(fieldName, comparisonValue, Expression.Equal(
            Expression.Convert(Expression.Property(e, fieldName), Expression.Constant(comparisonValue).Type),
            Expression.Constant(comparisonValue)), e);
    }

    private Expression<Func<T, bool>> GenerateConditionalExpression<T>(string fieldName, object comparisonValue, Expression actualExpression, ParameterExpression e)
    {
        Expression call = Expression.Condition(Expression.Equal(e, Expression.Constant(null)),
        Expression.NotEqual(e, Expression.Constant(null)),
        Expression.Condition(Expression.Equal(Expression.Property(e, fieldName), Expression.Constant(null)),
        Expression.NotEqual(e, Expression.Constant(null)), actualExpression));
        return Expression.Lambda<Func<T, bool>>(call, e);
    }

    private Expression<Func<T, bool>> GenerateGenericCallExpression<T>(string fieldName, object comparisonValue, string methodName, ParameterExpression e, bool equals = true)
    {
        PropertyInfo propertyInfo = typeof(T).GetProperty(fieldName);
        MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
        ConstantExpression c = Expression.Constant(comparisonValue, typeof(string));            
        MethodInfo mi = typeof(string).GetMethod(methodName, new Type[] { typeof(string) });

        if (equals)
        {
            Expression call = Expression.Condition(Expression.Equal(e, Expression.Constant(null)),
            Expression.NotEqual(e, Expression.Constant(null)),
            Expression.Condition(Expression.Equal(Expression.Property(e, fieldName), Expression.Constant(null)),
            Expression.NotEqual(e, Expression.Constant(null)), Expression.Call(m, mi, c)));
            return Expression.Lambda<Func<T, bool>>(call, e);
        }
        else
        {
            Expression call = Expression.Condition(Expression.Equal(e, Expression.Constant(null)),
            Expression.NotEqual(e, Expression.Constant(null)),
            Expression.Condition(Expression.Equal(Expression.Property(e, fieldName), Expression.Constant(null)),
            Expression.NotEqual(e, Expression.Constant(null)), Expression.Not(Expression.Call(m, mi, c))));
            return Expression.Lambda<Func<T, bool>>(call, e);
        }
    }


文章来源: Handle null ref exceptions in LINQ Expression calls