我得到 Nullable object must have a value after checking for null
在常规对象上,在 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));
我什至不确定原始问题中使用的版本是否有效,或者为什么有人想要使用它。)
最佳答案
与我在评论中写的相反,问题是查询提供者不甚至试图通过消除常量部分来减少谓词表达式。正如@Servy 在评论中正确指出的那样,他们并没有被迫这样做,并且一般来说可能有技术原因不这样做,但实际上人们倾向于在他们的查询表达式中使用这样的条件并期望它们作为 < strong>if 它们在 LINQ to Objects 中被评估。
我见过很多类似用法的问题,最后一个是 LINQ to Entities conditionals give weird results ,“标准”评论/答案是 - 使用链式 Where
和 if
或某些谓词构建器。然后我开始思考 - 好吧,供应商不这样做,那我们为什么不自己做 - 毕竟,我们是开发人员并且可以编写(一些)代码。所以我最终得到了以下扩展方法,它使用 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();
关于c# - "Nullable object must have a value"在非原始/非结构对象上检查 null 后出现异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36892232/