提问人:Nilesh Wagh 提问时间:4/8/2021 最后编辑:marc_sNilesh Wagh 更新时间:4/9/2021 访问量:175
如何在 c 中识别可能导致 NullReferenceException 的所有可能的代码块#
How to identify all possible code blocks which can cause the NullReferenceException in c#
问:
我在 .NET 4.7.2 中使用 C# 和 Entity Framework 的一个项目在多种情况下都会导致这种情况。代码库非常庞大,因此在使用之前分析代码和处理可为 null 的对象非常困难。NullReferenceException
当我们尝试使用实体框架访问给定实体的相关 null 实体或 null 实体集合(通过外键相关)时,会发生大多数异常。如果外键表中不存在数据,则该相关实体将存在或为 null。
我们假设相关对象过去一定存在,但现在表的大部分结构已经更改,这些对象现在可能不存在。
我的代码太长,很难分析和更改可为 null 的对象处理。
那么有人可以向我建议解决方案,该解决方案将给我一个将来会导致的代码块?NullReferenceException
有什么工具可以给我一个警告,在哪里会发生?NullReferenceException
答:
NullReferenceException
是你从第一天起就必须考虑的事情。在始终设置引用的假设下编写代码,这为错误敞开了大门,这些错误通常是将来出现的间歇性错误。AFAIK 没有针对该问题的“快速解决方案”,但您可以采取重构步骤来减少 Null 的影响,并帮助确保在调用堆栈实际有用的位置发生意外 null 值的异常:
#1.从 Linq 表达式中删除所有方法,这些方法未显式处理数据可能不会返回的事实。这意味着替换为或更好,您期望 1 条记录。您仍然会遇到异常,但会在读取实体时明确哪些条件无效,而不是稍后在代码中使用实体而不检查 #null。*OrDefault()
FirstOrDefault()
First()
Single()
#2.初始化实体中的集合属性。这有助于确保实体中的集合在创建新实体时“准备就绪”,以防接收实体的函数可能会获得新行而不是现有行。J.F.
public virtual ICollection<OrderLine> OrderLines { get; set; } = new List<OrderLine>();
#3.确保启用延迟加载,并将引用和集合标记为 。这绝不是理想的,但恕我直言,延迟加载调用比 .如果调用恰好在加载实体的 DbContext 范围之外进行,则仍会收到异常,但通常更容易确定访问的内容。virtual
NullReferenceException
#4.添加快速失败验证。在接受存储的构造函数参数时,请添加 null 检查断言。对于接受任何可能为 null 的内容的方法,请添加断言。你会得到一些例外,但有意义的例外会帮助你识别错误的来源。例如:
// Constructor injected references:
oublic OrderService(IOrderRepository repository)
{
// change this:
// _repository = repository;
// ... to this:
_repository = repository ?? throw new ArgumentNullException("repository");
}
// Parameter assertion:
public void AddCustomer(Customer customer)
{
// add these to assert your parameters.
if(customer == null) throw new ArgumentNullException("customer");
// ...
}
这些通常是快速的重构,可以在整个现有应用程序中非常不显眼地应用。
#5.旨在消除除插入新实体之外构造实体的任何代码。我遇到的常见例子是:
.Select(x => new Order { /* populating some Order columns */ })
其中 是 Entity 类,但像视图模型或其他一些临时数据容器一样使用。任何其他代码,如果不是出于使用目的,那么它就应该被重构,并带有偏见。实体类应始终反映数据的完整或可完成的表示形式。(可完成意味着被跟踪的实体仍在其 DbContext 范围内,并且能够在需要时延迟加载)任何被调用接受 Order 实体的函数都应该收到一个完整且有效的 Order,其中包含所有引用,无论是 eager load 还是 lazy loadable。传递的所有实体也应全部关联到同一个 DbContext,因此理想情况下,应将实体序列化到 Web 客户端和从 Web 客户端序列化实体的代码,以使用投影,或者非常谨慎地确保实体重新关联到当前作用域的 DbContext。Order
new
context.Orders.Add(order);
归根结底,你面临的是一种形式的技术债务,就像所有债务一样,你早期在通过假设节省开发时间方面获得的“信用”会产生利息,这将增加以后追踪错误并最终“修复”错误假设的时间。您需要从童子军的角度来看待应用程序,并致力于让它比开始时更干净。
评论
First()
FirstOrDefault()
评论
null