" Nullable对象必须具有值"在非原始/非结构对象上检查null之后发生异常

时间:2016-04-27 14:06:05

标签: c# lambda devart

在空检查后,我在常规对象上获得Nullable object must have a value after checking for null。我发现了各种问题,主要是关于linq-to-sql,有相同的问题,但总是有可空的原始类型(如bool?DateTime?)。

在我的案例中导致异常的行如下所示:

myDataContext.Orders.Where(y => customer.Address == null || (string.IsNullOrEmpty(customer.Address.Street) || y.Customers.Addresses.Street == customer.Address.Street)))

customer课程如下所示:

public class Customer
{
    private Address address = null;
    public Address Address{get{return address;} set{address=value;}}
}

address属性如下所示:

public class Address
{
    private string street = null;
    public string Street{get{return street ;} set{street =value;}}
}

如果我用以下代码替换上面的代码:

string custStreet = null;
if (customer.Address != null)
{
    custStreet = customer.Address.Street;
}

myDataContext.Orders.Where(y =>(customer.Address == null || (string.IsNullOrEmpty(custStreet) || y.Customers.Addresses.Street == custStreet)))

运行正常。我不明白其原因。在执行Lambda语句本身之前,我也不想定义无数变量。

请注意,上面的Lambda语句是更大的Lambda Where子句的一部分,该子句包含更多此类语句。我知道我可以使用表达式树,但编码了这么远,我真的不想现在切换。

修改

当问题得到解答时,我会告诉你我是如何解决这个问题的:我自己构建了一个递归属性初始化器。对Activator类抛出不是字符串,列表/数组或基本类型的所有内容。我从here得到了这个想法并对其进行了一些更改(基本上,忽略了所有不需要初始化的内容而不是Activator.CreateInstance(Type.GetType(property.PropertyType.Name));我使用Activator.CreateInstance(property.PropertyType));我&#39 ; m甚至不确定原始问题中使用的版本是否有用或为什么有人想要使用它。)

2 个答案:

答案 0 :(得分:2)

与我在评论中写的相反,问题是查询提供程序确实甚至尝试通过消除常量部分来减少谓词表达式。正如@Servy在评论中正确指出的那样,他们并没有被迫这样做,并且通常说可能有技术原因没有这样做,但实际上人们倾向于在他们的查询表达式中使用这些条件并期望它们像< strong> if 它们在LINQ to Objects中进行评估。

我见过很多类似用法的问题,最后一个是LINQ to Entities conditionals give weird results,“标准”评论/回答是 - 使用链接Whereif或一些谓词构建器。然后我开始思考 - 好吧,提供商不这样做,那么为什么我们不这样做呢 - 毕竟,我们是开发人员,可以写(某些)代码。所以我最终得到了以下扩展方法,该方法使用ExpressionVisitor来修改查询表达式树。我想将它发布到链接的问题,但是因为我已经以某种方式参与了这个主题,所以你走了:

public static class QueryableExtensions
{
    public static IQueryable<T> ReduceConstPredicates<T>(this IQueryable<T> source)
    {
        var reducer = new ConstPredicateReducer();
        var expression = reducer.Visit(source.Expression);
        if (expression == source.Expression) return source;
        return source.Provider.CreateQuery<T>(expression);
    }

    class ConstPredicateReducer : ExpressionVisitor
    {
        private int evaluateConst;
        private bool EvaluateConst { get { return evaluateConst > 0; } }
        private ConstantExpression TryEvaluateConst(Expression node)
        {
            evaluateConst++;
            try { return Visit(node) as ConstantExpression; }
            catch { return null; }
            finally { evaluateConst--; }
        }
        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (EvaluateConst || node.Type == typeof(bool))
            {
                var operandConst = TryEvaluateConst(node.Operand);
                if (operandConst != null)
                {
                    var result = Expression.Lambda(node.Update(operandConst)).Compile().DynamicInvoke();
                    return Expression.Constant(result, node.Type);
                }
            }
            return EvaluateConst ? node : base.VisitUnary(node);
        }
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (EvaluateConst || node.Type == typeof(bool))
            {
                var leftConst = TryEvaluateConst(node.Left);
                if (leftConst != null)
                {
                    if (node.NodeType == ExpressionType.AndAlso)
                        return (bool)leftConst.Value ? Visit(node.Right) : Expression.Constant(false);
                    if (node.NodeType == ExpressionType.OrElse)
                        return !(bool)leftConst.Value ? Visit(node.Right) : Expression.Constant(true);
                    var rightConst = TryEvaluateConst(node.Right);
                    if (rightConst != null)
                    {
                        var result = Expression.Lambda(node.Update(leftConst, node.Conversion, rightConst)).Compile().DynamicInvoke();
                        return Expression.Constant(result, node.Type);
                    }
                }
            }
            return EvaluateConst ? node : base.VisitBinary(node);
        }
        protected override Expression VisitConditional(ConditionalExpression node)
        {
            if (EvaluateConst || node.Type == typeof(bool))
            {
                var testConst = TryEvaluateConst(node.Test);
                if (testConst != null)
                    return Visit((bool)testConst.Value ? node.IfTrue : node.IfFalse);
            }
            return EvaluateConst ? node : base.VisitConditional(node);
        }
        protected override Expression VisitMember(MemberExpression node)
        {
            if (EvaluateConst || node.Type == typeof(bool))
            {
                var expressionConst = node.Expression != null ? TryEvaluateConst(node.Expression) : null;
                if (expressionConst != null || node.Expression == null)
                {
                    var result = Expression.Lambda(node.Update(expressionConst)).Compile().DynamicInvoke();
                    return Expression.Constant(result, node.Type);
                }
            }
            return EvaluateConst ? node : base.VisitMember(node);
        }
        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            if (EvaluateConst || node.Type == typeof(bool))
            {
                var objectConst = node.Object != null ? TryEvaluateConst(node.Object) : null;
                if (objectConst != null || node.Object == null)
                {
                    var argumentsConst = new ConstantExpression[node.Arguments.Count];
                    int count = 0;
                    while (count < argumentsConst.Length && (argumentsConst[count] = TryEvaluateConst(node.Arguments[count])) != null)
                        count++;
                    if (count == argumentsConst.Length)
                    {
                        var result = Expression.Lambda(node.Update(objectConst, argumentsConst)).Compile().DynamicInvoke();
                        return Expression.Constant(result, node.Type);
                    }
                }
            }
            return EvaluateConst ? node : base.VisitMethodCall(node);
        }
    }
}

使用这种扩展方法,您只需在查询结束时插入.ReduceConstPredicates()AsEnumerable()之前,ToList之类的相似):

var query = myDataContext.Orders
    .Where(y => customer.Address == null || string.IsNullOrEmpty(customer.Address.Street) || y.Customers.Addresses.Street == customer.Address.Street)
    .ReduceConstPredicates();

答案 1 :(得分:1)

在将查询转换为SQL之前,必须将表达式customer.Address.Street 的值计算为其值*。该表达式不能留在数据库的基础SQL中,以便可能或不可能计算为值。查询提供程序必须无条件地对其进行评估,以确定SQL应该是什么样子。所以,是的,您需要执行表达式的外部的空检查。当然有很多方法可以做到这一点,但是空检查逻辑 需要在查询提供者翻译的表达式之外。