处理LINQ Expression调用中的null ref异常 [英] Handle null ref exceptions in LINQ Expression calls

查看:86
本文介绍了处理LINQ Expression调用中的null ref异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个通用的Expression构建器,只要对象值都不为null,该构建器就可以正常工作.

I am trying to create a generic Expression builder, which basically works fine as long as none the objects values is null.

我当前的代码如下(以StartsWith为例):

My current code looks like this (StartsWith as an example):

                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);
                }

让我们假设字段是属性CustomerName.我了解实际的最终表达将是:

Let's assume field is the Property CustomerName. I understand that the actual final expression will be like:

e.CustomerName.StartsWith(val)

如果CustomerName为null,则当然不会调用StartsWith方法,这很清楚.

and if CustomerName is null it will, of course, fail to call the StartsWith Method, which is perfectly clear.

我试图做这样的事情:

                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'异常.

But this produces a Expression of type 'System.Void' cannot be used for return type 'System.Boolean' Exception.

到目前为止,我有点迷失了.也许你们可以将我推向正确的方向.

I am a little bit lost as of now. Maybe you guys can push me in the right direction.

非常感谢!

推荐答案

现在可以正常使用了.非常感谢Servy将我推向正确的方向.

Got it working now. Big thanks to Servy for pushing me in the right direction.

Expression.Condition正是我实现目标所需要的.由于实际对象和对象的属性在运行时可以为null,因此我必须嵌套两个条件:

The Expression.Condition was exactly what I needed to achieve my goal. Since the actual object and the object's property can be null at run time I had to nest two Conditions:

            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);

如果为空值,我决定使用

In case of a null value I decided to use

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

,以确保接收到"false".我敢肯定有更好的方法可以做到这一点,但是我还没有想到.

to be sure to receive "false". I'm sure there is a nicer way to do this, but I could not think of one, yet.

最终表达式将如下所示

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

到目前为止,尽管我会尝试对其进行优化,但我对该解决方案感到非常满意.

As of now I am pretty happy with the solution, though I'll try to optimize it.

我当前的代码实现和用法如下所示.也许对你们中的一个有帮助:

My current implementation and usage of the code looks like this. Maybe it helps one of you guys:

        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);
        }
    }

这篇关于处理LINQ Expression调用中的null ref异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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