Search code examples
c#dependency-injectionninjectanti-patternsservice-locator

How do I NOT use DependencyResolver.Current.GetService(...) in this situation


Following the advice I have been given in this thread [Ninject UOW pattern, new ConnectionString after user is authenticated I now understand that I should not use the following line...

    var applicationConfiguration =
            (IApplicationConfiguration)
                DependencyResolver.Current.GetService(typeof(IApplicationConfiguration));

...as a Service Locator is an anti-pattern.

But in the case of the following procedure how can I instantiate my concrete object that implements "IApplicationConfiguration" so that I can use that object to get the unknown user role name, or use it to assign to the "ApplicationConfiguration" property of my principle?

Global.asax

public class MvcApplication : NinjectHttpApplication
{
    /// <summary>
    /// Handles the PostAuthenticateRequest event of the Application control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
        String[] roles;
        var applicationConfiguration =
            (IApplicationConfiguration)
                DependencyResolver.Current.GetService(typeof(IApplicationConfiguration));
        var identity = HttpContext.Current.User.Identity;
        if (Request.IsAuthenticated)
        {
            roles = Roles.GetRolesForUser(identity.Name);
        }
        else
        {
            roles = new[] { applicationConfiguration.UnknownUserRoleName };
        }
        var webIdentity = new WebIdentity(identity, roles);
        var principal = new WebsitePrincipal(webIdentity)
        {
            ApplicationConfiguration = applicationConfiguration
        };

        HttpContext.Current.User = principal;
    }
    .
    .
    .
}

Resolution Mapping Code

    public class ApplicationConfigurationContractMapping : NinjectModule
{
    public override void Load()
    {
        Bind<IApplicationConfiguration>()
            .To<ApplicationConfiguration>();
    }
}

ApplicationConfiguration

public class ApplicationConfiguration : IApplicationConfiguration
{
    .
    .
    .
    .
}

I am using Ninject as my Dependency Injection framework. Any suggestions appreciated.

EDIT: Full code can be seen here: https://github.com/dibley1973/Dibware.Template.Presentation.Web


Solution

  • You can't prevent having to call either the DI Container or an abstraction over it in your Application_PostAuthenticateRequest, but that shouldn't be a problem, since this Application_PostAuthenticateRequest can be considered to be part of your Composition Root. Or in other words: you have to resolve it somewhere.

    The problem in your case, however, is that this method contains an awful lot of code, and the reason for this is that you are missing an abstraction. To resolve this, extract all logic of this method into a new class and hide it behind an abstraction. What will be left is the following code:

    protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
    {
       var provider = (IPostAuthenticateRequestProvider)
           DependencyResolver.Current.GetService(typeof(IPostAuthenticateRequestProvider));
    
       provider.ApplyPrincipleToCurrentRequest();
    }
    

    The code can be built up by your DI Container, and will have the following signature:

    public class MvcPostAuthenticateRequestProvider : IPostAuthenticateRequestProvider
    {
        private readonly IApplicationConfiguration configuration;
    
        public MvcPostAuthenticateRequestProvider(IApplicationConfiguration configuration)
        {
            this.configuration = configuration;
        }
    
        public void ApplyPrincipleToCurrentRequest()
        {
            // ...
        }
    }