1

Initial Question

I'm calling this serviceProvider.GetRequiredService<SignInManager<IdentityUser>>() and am receiving the following error. Why doesn't the dependency inject take care of supplying a non-null contextAccessor?

System.ArgumentNullException was unhandled by user code
  HResult=-2147467261
  Message=Value cannot be null.
Parameter name: contextAccessor
  ParamName=contextAccessor
  Source=Microsoft.AspNet.Identity
  StackTrace:
       at Microsoft.AspNet.Identity.SignInManager`1..ctor(UserManager`1 userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory`1 claimsFactory, IOptions`1 optionsAccessor, ILogger`1 logger)
    --- End of stack trace from previous location where exception was thrown ---
       at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
       at Microsoft.Framework.DependencyInjection.ServiceLookup.Service.ConstructorCallSite.Invoke(ServiceProvider provider)
       at Microsoft.Framework.DependencyInjection.ServiceProvider.ScopedCallSite.Invoke(ServiceProvider provider)
       at Microsoft.Framework.DependencyInjection.ServiceProvider.<>c__DisplayClass8_0.<RealizeService>b__0(ServiceProvider provider)
       at Microsoft.Framework.DependencyInjection.ServiceProvider.GetService(Type serviceType)
       at Microsoft.Framework.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
       at Microsoft.Framework.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
       at ResourceOwnerPasswordFlow.Startup.<>c__DisplayClass6_0.<Configure>b__1(OpenIdConnectServerOptions options) in C:\Users\BigFont\Documents\GitHub\AspNet.Security.OpenIdConnect.Server\samples\ResourceOwnerPasswordFlow\Startup.cs:line 109
       at Microsoft.Framework.OptionsModel.ConfigureOptions`1.Configure(TOptions options, String name)
       at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1..ctor(RequestDelegate next, IOptions`1 options, ILoggerFactory loggerFactory, IUrlEncoder encoder, ConfigureOptions`1 configureOptions)
       at AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerMiddleware..ctor(RequestDelegate next, ILoggerFactory loggerFactory, IDistributedCache cache, IHtmlEncoder htmlEncoder, IUrlEncoder urlEncoder, IDataProtectionProvider dataProtectionProvider, IOptions`1 options, ConfigureOptions`1 configuration) in C:\Users\BigFont\Documents\GitHub\AspNet.Security.OpenIdConnect.Server\src\AspNet.Security.OpenIdConnect.Server\OpenIdConnectServerMiddleware.cs:line 38
  InnerException: 

Research

I've seen the constructor injection done in the sample project here and it just works. What am I missing?

public ManageController(
    UserManager<ApplicationUser> userManager, 
    SignInManager<ApplicationUser> signInManager)
{
    UserManager = userManager;
    SignInManager = signInManager;
}

I've also seen in the Music Store Tests the explicit addition of an HttpContextAccessor into the service collection. Do I need to do this also? How come?

// IHttpContextAccessor is required for SignInManager, and UserManager
services.AddInstance<IHttpContextAccessor>(
    new HttpContextAccessor()
        {
            HttpContext = new TestHttpContext(),
        });
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467

1 Answers1

1

serviceProvider.GetRequiredService< SignInManager< IdentityUser>>()

So it looks like you are using service locator anti pattern which is ok if you must but it seems like you are requesting the wrong type shouldn't you be using:

serviceProvider.GetRequiredService< SignInManager< ApplicationUser>>()

Since that is the type that is being injected into ManageController and you know already that the DI can resolve it?

I can tell you that in my own code I'm using constructor injection and one of my classes takes IHttpContextAccessor in its constructor and it gets injected just fine without me having to register that type with the service collection so I think it gets added elsewhere from Microsoft code since they also depend on it in identity classes

I don't think you should add an instance of HttpContext to services collection. If you need to access HttpContext then you should declare a constructor dependency if possible on IHttpContextAccessor which will then enable you to access the current HttpContext

For example in my code I have a constructor like this:

public RequestSiteResolver(
        ISiteRepository siteRepository,
        IConfiguration configuration,
        IHttpContextAccessor httpContextAccessor)
    {
        contextAccessor = httpContextAccessor;
        siteRepo = siteRepository;
        config = configuration;

    }

and IHttpContextAccessor does get injected without me having to explicitly add it to the service container. Then I can use it later to access the current httpContext like so:

string host = contextAccessor.HttpContext.Request.Host.Value;
alan
  • 6,705
  • 9
  • 40
  • 70
Joe Audette
  • 35,330
  • 11
  • 106
  • 99
  • In my own code, I'm using `IdentityUser` instead of extending it with something like `ApplicationUser`. – Shaun Luttin Jul 06 '15 at 15:52
  • The `ManageController` isn't from my code; rather, it's from an example of code from the docs. – Shaun Luttin Jul 06 '15 at 15:54
  • I'm not sure that the service locator is an anti-pattern. http://stackoverflow.com/questions/22795459/is-servicelocator-anti-pattern It just seems like something we ought to use thoughtfully. – Shaun Luttin Jul 06 '15 at 15:56