我正在使用.NET Core构造函数注入。 在同事的代码审查中,他提出了一个问题,即我是否应该检查控制器中注入的依赖项是否为空值。
由于框架负责创建服务的实例,因此在我看来,它将处理所有错误,并且永远不会将空值依赖项传递给构造函数。我对此没有任何事实证据,所以我想知道是否有必要进行空检查。
例如,我是否应该检查以下代码中的“ myService”是否为空? (假设代码配置为使用DI)
public class MyController
{
private readonly IMyService _myService;
public MyController(IMyService myService)
{
_myService = myService;
}
}
答案 0 :(得分:4)
是否需要使用构造函数注入检查空值?
要视情况而定。
不要。这不是必需的。该框架不允许这样做。
然后执行。手动实例化会在某个地方导致NullReferenceException,并且很难找到。
就是这样,使用类似这样的内容:
public MyController(IMyService myService)
{
if (myService == null)
{
throw new ArgumentNullException(nameof(myService));
}
_myService = myService;
}
这是一张非常便宜的支票,如果有人出于某种原因通过null
,则更容易追踪。
答案 1 :(得分:1)
为避免明显的干扰,在构造函数中执行null检查没有任何危害。
有两种从框架中获取DI服务的方法。
第一个是GetService<T>()
。如果尚未注册该服务,则此 将返回空值。
第二个是GetRequiredService<T>()
。如果找不到该服务,此will throw an exception。
如果这两种方法无法完全实例化您要的服务,则它们都会引发异常。
class Program
{
static void Main(string[] args)
{
var services = new ServiceCollection()
.AddTransient<IServiceB, ServiceB>()
.BuildServiceProvider();
var servB = services.GetService<IServiceB>();
}
}
public interface IServiceA { }
public interface IServiceB { }
public class ServiceA : IServiceA { }
public class ServiceB : IServiceB { public ServiceB(IServiceA a) { } }
在此示例中,ServiceB
需要一个IServiceA
,但是A
未添加到依存关系图中。最后一个方法将引发异常:
System.InvalidOperationException:“在尝试激活'DITests.ServiceB'时,无法解析类型为'DITests.IServiceA'的服务。'
如果我改而services.GetService<IServiceA>()
,我将得到一个空值。
您可以通过查看GitHub source code来亲自查看。当调用这两种方法时,它最终都会变成CreateConstructorCallSite
方法的方式。如果无法解析您的类型的依赖项,则会引发异常。
对于ASP.Net Core MVC,它使用GetRequiredService<>()
从DI图中获取控制器。
总而言之,不,如果您使用纯Microsoft DI框架,则无需 对构造函数中的DI对象执行空检查。正如Camilo Terevinto所说,该框架不允许这样做。
如您的帖子中所述,我还没有看到Microsoft书面文件明确表明您不需要
如果要传递IServiceProvider
作为构造函数参数,并且要从构造函数中解析服务,那么然后您将需要进行空检查。
答案 2 :(得分:0)
来自Camilo Terevinto或gunr2171的这些答案对您都有好处。
通常,我们可以讨论将一个例外与另一个例外(从NullReferenceException
到ArgumentNullException
)进行交易还是完全达成交易。问问自己,后者是否为您带来任何新信息。我的拙见是否。
一种或另一种方式,您会立即知道特定情况下的问题是什么,在哪里。在控制器的构造函数中执行null检查只是开销。这就是Camilo Terevinto的答案失败的地方。您永远不会自己启动它,也永远不会将其放入任何库代码中。 .NET Core中的内置DI服务旨在永远不要在构造函数中注入null
。
gunr2171的答案是关于DI的完全不同的方面。这是一个有效的答案,但针对另一个问题。
超过80%的DI使用是构造函数注入。这些注入大多数与MVC控制器和Razor页面中的视图模型有关。 MVC是ASP.NET Core 3.x和replaced by Razor Pages中已弃用的设计模式,但逻辑几乎相同。
任何可用的控制器至少需要三个依赖项:
在这里推我,您不想输入:
_logger = logger ?? throw new ArgumentNullException(...)
_mapper = mapper ?? throw new ArgumentNullException(...)
_repository = repository ?? throw new ArgumentNullException(...)
针对您拥有的每个控制器/视图模型。
绝对有更好的方法来做到这一点。我个人的建议是this,重复一个问题。