6

I have AuthenticationStrategy class which I'm going to inject in controller constructor.

I have two IAuthenticationProviders: InternalAuthenticationProvider and ExternalAuthenticationProvider.
In AuthenticationStrategy constructor I want to inject all providers.
Sample code:

public class AuthenticationStrategy
{
    private readonly Dictionary<string, IAuthenticationProvider> _authenticationProviders;

    public AuthenticationStrategy(IAuthenticationProvider[] authenticationProviders)
    {
        if (authenticationProviders == null)
        {
            throw new ArgumentNullException("AuthenticationProviders");
        }

        _authenticationProviders = authenticationProviders
            .ToDictionary(x => nameof(x), x => x);
    }
}

How can I inject multiple providers using depency injection? Sample code:

services.AddScoped<IAuthenticationProvider, InternalAuthenticationProvider>();
services.AddScoped<IAuthenticationProvider, ExternalAuthenticationProvider>();
services.AddScoped<AuthenticationStrategy>();

Any ideas?

2
  • Are you getting any errors? what happens when you try the above code? Commented Mar 28, 2017 at 13:00
  • IAuthenticationProvider[] authenticationProviders is null in AuthenticationStrategy constructor. Commented Mar 29, 2017 at 7:27

5 Answers 5

2

I think that storing the Dictionary inside your strategy is quite a code smeel, as it looks like an anti-pattern Service Locator. You probably need to introduce the factory for your authentication providers based on key. This is a desired approach in .Core dependency injection, however you can use other IoC containers with similar features (named dependencies, for example).

So, your code could be like this:

public enum AuthType
{
    Internal,
    External,
}

public interface IAuthenticationProviderResolver
{
    IAuthenticationProvider GetAuthByType(AuthType type);
}

public class ProviderResolver : IAuthenticationProviderResolver
{
    private readonly IServiceProvider _serviceProvider;

    public RepositoryResolver(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IAuthenticationProvider GetAuthByName(AuthType type)
    {
         switch (type) 
         {
             case AuthType.Internal:
                 return _serviceProvider.GetService<InternalAuthenticationProvider>();
             case AuthType.External:
                 return _serviceProvider.GetService<ExternalAuthenticationProvider>();
             default:
                 throw new ArgumentException("Unknown type for authentication", nameof(type))
         }
    }
}

Now all you need is to register your classes as usual:

services.AddSingleton<IAuthenticationProviderResolver, ProviderResolver>();
services.AddScoped<InternalAuthenticationProvider>();
services.AddScoped<ExternalAuthenticationProvider>();
services.AddScoped<AuthenticationStrategy>();

And usage inside the strategy:

public class AuthenticationStrategy
{
    private readonly IAuthenticationProviderResolver _resolver;

    public AuthenticationStrategy(IAuthenticationProviderResolver resolver)
    {
        if (resolver== null)
        {
            throw new ArgumentNullException("Provider Resolver");
        }

        _resolver = resolver;
    }

    public void MakeDecision()
    {
        _resolver.GetAuthByType(authType).Authenticate();
    }
}
Sign up to request clarification or add additional context in comments.

7 Comments

2 Things: 1) Using name (string) is loosely typed in IAuthenticationProvider GetAuthByName(string name), im not sure if its a better approach. 2) What if there are more than 2 concrete implementations of IAuthenticationProvider? - Thanks in advance!
Named dependencies is a common approach, and is quite similar with a Distionary. You still can define your own signature, with more arguments or with a enum
@YawarMurtaza updated to enum, as it's more type-safe approach, and added some switch. If you have some more thoughts regarding the solution, please share it. For me, it probably better to switch off to some other IoC container rather than native.
That makes it strongly typed which is good and answers my first point. Now what if we have another implementation of IAuthenticationProvider, we will have to edit this class to add another switch-case statement and add another enum value which violates open-close principle.
@YawarMurtaza I disagree with you here. Strategy should define the AuthType, and resolver should provide authentication needed by that type. If we'll store all of that inside strategy, after some time it will be like God object. Still think that some third-party IoC is more freferrable for this rather than such workaround
|
1

One option would be to make AuthenticationStrategy generic. Then you could differ with type

config.Scan(assembly =>
{
    assembly.AssemblyContainingType(typeof(AuthenticationProvider));
    assembly.ConnectImplementationsToTypesClosing(typeof(IAuthenticationProvider<>));
});

Code above scans the dll so you also don't have to register each and every one.

Comments

1

If you're sticking with the OOTB dependency injection setup i.e. not using a third party container then one option would be to be explicit in your constructor args like this:

public class AuthenticationStrategy
{
    public AuthenticationStrategy(
        IInternalAuthenticationProvider internal,
        IExternalAuthenticationProvider external)
    {
        ...
    }
}

IInternalAuthenticationProvider and IExternalAuthenticationProvider interfaces are nothing more than marker interfaces like so:

public interface IInternalAuthenticationProvider : IAuthenticationProvider { }
public interface IExternalAuthenticationProvider : IAuthenticationProvider { }

So your DI setup will now look like this:

services.AddScoped<IInternalAuthenticationProvider , InternalAuthenticationProvider>();
services.AddScoped<IExternalAuthenticationProvider , ExternalAuthenticationProvider>();
services.AddScoped<AuthenticationStrategy>();

1 Comment

This is a solution that I'm using right now. But still I want to get rid of explicit arguments in constructor because it could be more concrete providers in the future.
1

Assuming you are using Asp.Net Core project type with Visual Studio 2017

Lets say you have the following defination of an interface:

   public interface IAuthenticationProvider 
    {
    }

with implementing classes like so:

public class WindowsAuthentication : IAuthenticationProvider { }


public class NTMLAuthentication : IAuthenticationProvider { }


public class KerberosAuthentication : IAuthenticationProvider { }


public class CustomAuthentication : IAuthenticationProvider { }

So far so good. Now to resolve the dependencies for the types implementing the same interface I would use the custom resolver class with its interface:

public interface IAuthenticationResolver
{
    IAuthenticationProvider GetProvider(Type type);
}

and its implementation:

public class AuthenticationResolver : IAuthenticationResolver
    {
        private readonly IServiceProvider services;
        public AuthenticationResolver(IServiceProvider services)
        {
            this.services = services;
        }

        public IAuthenticationProvider GetProvider(Type type)
        {            
            return this.services.GetService(type) as IAuthenticationProvider;
        }
    }

In your Startup class, under ConfigureServices register these types

 services.AddTransient<IAuthenticationResolver, AuthenticationResolver>();
            services.AddTransient<WindowsAuthentication>();
            services.AddTransient<KerberosAuthentication>();
            services.AddTransient<NTMLAuthentication>();
            services.AddTransient<CustomAuthentication>();

Of course you can use Scopped if thats what you need.

Once this is all set, go back to your controller / client class where the dependencies are injected:

 public class HomeController : Controller
 {
        private readonly Dictionary<string, IAuthenticationProvider> authProvidersDictionary;

        public HomeController(IAuthenticationResolver resolver)
        {
            System.Reflection.Assembly ass = System.Reflection.Assembly.GetEntryAssembly();
            this.authProvidersDictionary = new Dictionary<string, IAuthenticationProvider>();

            foreach (System.Reflection.TypeInfo ti in ass.DefinedTypes)
            {
                if (ti.ImplementedInterfaces.Contains(typeof(IAuthenticationProvider)))
                {                   

                    this.authProvidersDictionary.Add(ti.Name, resolver.GetProvider(ti.UnderlyingSystemType));
                }
            }            
        }
}

Hope this helps!

Comments

0

The question is in itself an anti-pattern example of dependency injection being the "Golden Hammer" tool of choice for .net core.

Your class should be able to access the two authentication providers without the disjoint code of dependency injection.

Disjoint code in .net dependency injection:

  • Registering the injected object in the application's startup
  • Object construction of injected object separated from that object's implementation in an anonymous method
  • Object construction never directly called by the rest of the .net core's application code. The .net core framework calls the constructor.

A junior fresh out of school developer should be able to look at any line in any method and quickly find how that method is called, where the method is called and why the method is called -without having to know dozens (hundreds?) of small micro-features of the .net core framework.

Unit testing with mocked classes is easily achievable in ways other than using dependency injection.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.