Is it necessary to check null values with constructor injection?

浪子不回头ぞ 提交于 2019-12-11 13:05:42

问题


I'm using .NET Core constructor injection. In a code review from a colleague, he raised the question if I should check for null values on injected dependencies in controllers.

Since the framework is responsible for creating an instance of the service, it seems to me that it would take care of any errors and never have a null value dependency passed to a constructor. I don't have any factual evidence for this though, so I'd like to know if it's possible that a null check may be necessary.

For example, should I check if 'myService' is null in the following code? (Assuming the code is configured to use DI)

public class MyController
{
    private readonly IMyService _myService;

    public MyController(IMyService myService) 
    {
        _myService = myService;
    }
}

回答1:


Is it necessary to check null values with constructor injection?

It depends.

  • Is this internal code, used by you and (possibly) some teammates in a (fortunately) code-review environment?

Don't. It's not necessary. The framework doesn't allow this.

  • Is this code in a public library, used by multiple people or not actually following Dependency Injection?

Then do it. A manual instantation would result in a NullReferenceException somewhere and those are hard to track down.

That said, using something like this:

public MyController(IMyService myService) 
{
    if (myService == null)
    {
        throw new ArgumentNullException(nameof(myService));
    }

    _myService = myService;
}

Is an extremely cheap check and it's much easier to track down if someone passes null for some reason.




回答2:


To get the obvious out of the way, there is no harm in doing a null check in your constructor.


There are two methods for obtaining DI services from the framework.

The first is GetService<T>(). This will return a null value if such a service has not been registered.

The second is GetRequiredService<T>(). This will throw an exception if the service can't be found.

Both of these methods throw exceptions if they can't fully instantiate the service you are asking for.

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) { } }

In this example, ServiceB requires an IServiceA, but A is not added to the dependency graph. The last method will throw an exception:

System.InvalidOperationException: 'Unable to resolve service for type 'DITests.IServiceA' while attempting to activate 'DITests.ServiceB'.'

If I instead did services.GetService<IServiceA>(), I'd get a null value.


You can see this for yourself by looking at the GitHub source code. When calling either method, it will eventually make it's way to the CreateConstructorCallSite method. This throws an exception if it's unable to resolve the dependencies of your type.


As for ASP.Net Core MVC, it uses GetRequiredService<>() for getting your controllers from the DI graph.


In conclusion, no, you do not need to perform null checks for DI objects in your constructor if you're using the pure Microsoft DI framework. As Camilo Terevinto said, the framework does not allow it.

As noted in your post, I have not seen a written Microsoft document that explicitly says you do not need to

If you are passing in an IServiceProvider as a constructor argument and you're resolving services from within the constructor, then you'll need to do null checks.



来源:https://stackoverflow.com/questions/52880470/is-it-necessary-to-check-null-values-with-constructor-injection

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!