I use .NET Core WebAPI with dependency injection and multiple authentication schemas (http basic, access keys, JWT). I inject some business services which require some authenticated user data. If user is authenticated by any of auth middleware, DI works fine. If the user is not authenticated, DI cannot resolve some services. I need DI to return null
.
How is that possible? Code bellow will result in exception, null
is not allowed as result.
services.AddTransient<IMasterRepository>(serviceProvider =>
{
var _serviceFactory = new RepositoriesFactory(Configuration);
if (!Authenticated)
{
return null;
}
return _serviceFactory.CreateMasterRepository();
});
Also, I cannot return 401 in auth middleware, because another middleware may success (expl: cannot return 401 in http basic auth middleware because next one, JWT, may success)
Also, I cannot add "authentication required" check after all auth middlewares because some controllers are public (no authentication / dependency injection required).
Any advice? Thanks!
There's no problem in registering an implementation as null
. It's only in resolving that you will have a problem.
In other words, if you register:
services.AddTransient<IMasterRepository>(provider => null);
And then try:
private readonly IMasterRepository _repository;
public SomeController(IMasterRepository repository)
{
_repository = repository;
}
You will get an InvalidOperationException
at runtime, with a message similar to:
Unable to resolve service for type 'MyApp.IMasterRepository' while attempting to activate 'MyApp.Controllers.SomeController'
However, there is a simple workaround. Rather than injecting the interface, inject an IEnumerable
of that interface:
private readonly IMasterRepository _repository;
public SomeController(IEnumerable<IMasterRepository> repositories)
{
_repository = repositories.First(); // (using System.Linq)
}
You might think it should be FirstOrDefault
, however there will indeed be a single item containing the null
you registered.
This approach works because DI in ASP.Net Core supports registering multiple implementations of a given type and doesn't distinguish between null
and object instances at time of registration.
Do keep in mind that even though this works, it's not recommended because now the _repository
variable is potentially nullable, and a null check must be used every time it is accessed. For example: if (_repository != null) { _repository.DoSomething(); }
or _repository?.DoSomething();
. Most people do not expect to write code like that.
This covers the DI part of the question. But if indeed the issue is strictly with auth then ste-fu's answer describes a more appropriate approach.
The default DI framework does not allow for the factory delegate to return null by design.
Consider null object pattern by creating a NullObject derived from the interface
public class NullRepository : IMasterRepository {
public static readonly IMasterRepository Empty = new NullRepository();
public NullRepository () { }
//...implement members that do nothing and may return empty collections.
}
that does nothing when invoked.
services.AddTransient<IMasterRepository>(serviceProvider => {
IMasterRepository result = NullRepository.Empty;
var _serviceFactory = new RepositoriesFactory(Configuration);
if (Authenticated) {
result = _serviceFactory.CreateMasterRepository();
}
return result;
});
Checking for null now becomes
//ctor
public SomeClass(IMasterRepository repository) {
if(repository == NullRepository.Empty)
//...throw
//...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With