C# LINQ,在模型中处理Null
我试图把平均数传递给我的观点。AverageRating是在Icollection of Review模型中查询项目的结果。审查模型具有评级属性。然而,我得到了这样的信息: System.ArgumentNullException 无论何时执行get,都会发生这种情况,这是可以理解的。但是,当我的代码如下所示时,如何最好地处理模型或其他地方的空异常:C# LINQ,在模型中处理Null,c#,asp.net,linq,C#,Asp.net,Linq,我试图把平均数传递给我的观点。AverageRating是在Icollection of Review模型中查询项目的结果。审查模型具有评级属性。然而,我得到了这样的信息: System.ArgumentNullException 无论何时执行get,都会发生这种情况,这是可以理解的。但是,当我的代码如下所示时,如何最好地处理模型或其他地方的空异常: public class MyModel { //Querying this navigation property
public class MyModel
{
//Querying this navigation property
public ICollection<Review> Reviews { get; set; }
public double? AverageRating
{
get
{
//check that this is not null / handle null
return Math.Round(Reviews.Average(c => c.Rating), 1);
}
}
}
return Math.Round(Reviews?.Average(c => c.Rating) ?? 0.0, 1);
return Math.Round(Reviews?.Average(c => c?.Rating ?? 0.0) ?? 0.0, 1);
您可以使用
DefaultIfEmpty
为空集合设置0
值,并且要从平均值
计算中排除可能的空值,您应该消除它们
public double? AverageRating
{
get
{
if (Reviews == null)
{
return null;
}
return Math.Round(Reviews.Where(x => x.Rating.HasValue).Select(x => x.Rating).DefaultIfEmpty(0).Average().Value, 1);
}
}
如果您使用的是C#6.0,那么可以使用null传播来帮助指定默认场景
代码最终将如下所示:
public class MyModel
{
//Querying this navigation property
public ICollection<Review> Reviews { get; set; }
public double? AverageRating
{
get
{
//check that this is not null / handle null
return Math.Round(Reviews.Average(c => c.Rating), 1);
}
}
}
return Math.Round(Reviews?.Average(c => c.Rating) ?? 0.0, 1);
return Math.Round(Reviews?.Average(c => c?.Rating ?? 0.0) ?? 0.0, 1);
这利用null传播来确保在访问Average
扩展方法之前,Reviews集合不为null
如果单个项为NULL,则可以在lambda内部扩展检查,并使用以下内容:
public class MyModel
{
//Querying this navigation property
public ICollection<Review> Reviews { get; set; }
public double? AverageRating
{
get
{
//check that this is not null / handle null
return Math.Round(Reviews.Average(c => c.Rating), 1);
}
}
}
return Math.Round(Reviews?.Average(c => c.Rating) ?? 0.0, 1);
return Math.Round(Reviews?.Average(c => c?.Rating ?? 0.0) ?? 0.0, 1);
这将防止审查为空或审查的项目为空
这里有一把小提琴在演奏:
如果需要跳过将空值转换为0,则可以先使用Where
语句从集合中删除空项
return Math.Round(Reviews?.Where(c => c?.Rating != null).Average(c => c.Rating) ?? 0.0, 1);
这样,在将其处理为Average
之前,将从列表中删除任何空项
编辑
根据下面的注释,您可以使用DefaultIfEmpty
处理序列本身为空的情况,如下所示:
return Math.Round(Reviews?.DefaultIfEmpty().Average(c => c?.Rating ?? 0.0) ?? 0.0, 1);
调用DefaultIfEmpty
将返回一个IEnumerable
将在其中包含一个null元素。然后在平均过程中将其过滤掉,并返回0
这也可以与本文中的其他方法相结合。fiddle已经更新为使用DefaultIfEmpty
什么是空值的测试示例?
首先,让我们假设null
表示unknown
。埃里克·利珀特(Eric Lippert)的研究提供了一个很好的理由。它可以进一步追溯到SQL的设计和三态逻辑的原理。null
集合不是空的,就像null
int?
不是零一样
但即使您不同意,正确使用空值也有两种基本理念:
1.防止空值
只需调整模型,以便在对象的生存期内始终防止空值。这并不总是可以通过类型系统实现(特别是在使用.NET序列化时)。这也可能会在某些地方产生大量额外的样板代码,因此请明智地使用它:
public class Model
{
// is non-null in any Model instance
public IReadOnlyList<ModelItem> Items { get; }
public Model(IEnumerable<ModelItem> items)
{
Items = new List<ModelItems>(items); // does not check if items contains null
}
}
请注意,Average
不会使用空序列抛出invalidoOperationException
。与不可为空的变量不同,应为不可为空的类型添加额外的检查
还要注意的是,代码没有试图将null
s解析为除null
以外的任何内容。如果您的null
在某个地方得到处理,它很可能是应用程序业务逻辑的一部分,并且应该位于相应的层中(例如,处理与没有特定属性的早期版本模型的向后兼容性的代码,将其作为null
返回)
但是,如果您的模型类固有地假设null
集合是空集合(出于可读性和维护原因,我强烈建议不要这样做),那么null确实不应该传播,应该在该类内部处理,例如使用合并操作符(??
).此实现可以满足您的需要:
public double? AverageRating
{
get
{
return Reviews?.Average(x => x?.Rating);
}
}
它将处理审核
为空
(由于在审核
后使用?
,它将返回空
)
由于在x
之后使用?
,因此它将处理为空的个人评论(在计算平均值时将忽略这些评论)。这种情况下的例外情况可能是因为Reviews
集合为null
,或者Reviews
集合项之一为null
。空集合和null
之间的语义区别是什么?包含null
元素的集合的含义是什么?@mjwills,Review.Rating是一个整数。@ZdeněkJelínek Jelínek我想如果一个Review集合项为null,则表示我还没有评级。@Sefe很好!谢谢。我建议使用null
,因为它更能反映“我不知道”而不是“非常低的分数”。使用返回Math.Round(Reviews?.Average(c=>c?.Rating?0.0)??0.0,1)代码>我得到无效操作异常:序列不包含任何元素
。我的视图:@Html.DisplayFor(modelItem=>item.AverageRating)
。这个问题的可能解决方案是什么?我有点不知所措,因为我对这一切都很陌生……我犯了一个错误<代码>平均值
需要至少包含一个元素的序列才能正确处理。解决方案是在调用Average
之前检查元素,或者使用DefaultIfEmpty
要求序列至少有一个元素才能正确处理。
请注意,并非所有序列都是如此(例如,可为null的int序列)。这是有效的!我现在可以看到在我看来,它是空的,这是我想要的平均评级。我想我会用三元组检查它是否为空,然后有一个像“还没有评级”这样的文本。谢谢你的努力,非常感谢!