C# 我应该在ASP.NET核心控制器中放置保护子句吗?

C# 我应该在ASP.NET核心控制器中放置保护子句吗?,c#,unit-testing,asp.net-core,C#,Unit Testing,Asp.net Core,从单元测试、一般开发和代码可读性的角度来看,我应该使用保护子句来检查注入控制器的空参数吗 或者我应该依赖ASP.NET核心框架,如果一切都正确配置(例如DI容器、模型绑定和其他配置),它将不会传递null或无效参数 以下是不带保护条款的控制器示例: [ApiController] 公共类家庭控制器:ControllerBase { 专用只读IRepository存储库; 专用只读IMapper\u映射器; 私有只读字符串\u消息; 公共HomeController(IRepository存储库、

从单元测试、一般开发和代码可读性的角度来看,我应该使用保护子句来检查注入控制器的空参数吗

或者我应该依赖ASP.NET核心框架,如果一切都正确配置(例如DI容器、模型绑定和其他配置),它将不会传递null或无效参数

以下是不带保护条款的控制器示例:

[ApiController]
公共类家庭控制器:ControllerBase
{
专用只读IRepository存储库;
专用只读IMapper\u映射器;
私有只读字符串\u消息;
公共HomeController(IRepository存储库、IMapper映射器、字符串消息)
{
_liveEventsRepository=存储库;
_映射器=映射器;
_消息=消息;
}
[HttpGet]
public IActionResult GetSomeData([FromQuery]QueryParameters参数)
{
var result=\u repository.GetData(参数);
var items=\u mapper.Map(result.MyItems);
返回Ok(新的
{
项目,
result.TotalItemsCount
});
}
}
如果我添加如下保护条款:

[ApiController]
公共类家庭控制器:ControllerBase
{
专用只读IRepository存储库;
专用只读IMapper\u映射器;
私有只读字符串\u消息;
公共HomeController(IRepository存储库、IMapper映射器、字符串消息)
{
if(存储库==null)
抛出新的ArgumentNullException(“存储库”);
if(映射器==null)
抛出新的ArgumentNullException(“映射器”);
if(String.IsNullOrWhiteSpace(message))
抛出新的ArgumentException(“文本”);
_liveEventsRepository=存储库;
_映射器=映射器;
_消息=消息;
}
[HttpGet]
public IActionResult GetSomeData([FromQuery]QueryParameters参数)
{
if(参数==null)
抛出ArgumentNullException(“参数”);
var result=\u repository.GetData(参数);
var items=\u mapper.Map(result.MyItems);
返回Ok(新的
{
项目,
result.TotalItemsCount
});
}
}
这是否为我的代码库增加了价值?或者我应该完全跳过ASP.NET核心控制器中的保护条款,考虑底层框架基础设施并假设所有内容都已正确设置


同样从单元测试的角度来看,我应该通过传递空/无效参数来测试控制器构造函数和操作,还是这是不必要的开销?

对此的回答将是自以为是的。这里没有必然正确或错误的答案。您当然可以相信控制器将被注入它所需要的任何依赖项。如果不能,将抛出异常

然而,出于两个原因,我仍然倾向于使用警卫。首先,我强烈支持一致性。如果你开始区分这个东西需要防护,而另一个东西不需要,那么不可避免的是,你不会总是想增加防护,并且会把它们放在它们应该放的地方。如果你只是一直在增加守卫,那么你的行为是一致的,你永远不会错过任何东西

其次,当进行单元测试时,您将负责填充依赖项(在某种程度上或其他程度上),因此guard子句充当测试代码的健全性检查。如果您忘记满足某个依赖项,您将确切地知道代码失败的原因,而不是在实际尝试使用该依赖项之后在代码中导致的一些随机错误

简言之,这是一个成本效益结论。添加保护条款需要多少努力?如果他们真的不需要,他们当然不会伤害任何东西,但是他们在那里有什么好处呢?好的,一致性和健全性检查,这实际上是非常有价值的事情,当涉及到跟踪bug和维护代码时

值得一提的是,您应该真正使用throw表达式,因为它更简洁、更容易,这使得执行该操作的决定变得更容易:

_liveEventsRepository = repository ?? throw new ArgumentNullException(nameof(repository));
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
_message = !string.IsNullOrWhiteSpace(message) ? message : throw new ArgumentException("Value must not be null or whitespace.", nameof(message));

在构造函数中防范DI提供的服务和在操作中防范用户提供的值有很大区别