C# – Asp.Net Core 2.1 Identity – UserStore dependency injection

asp.netasp.net-coreasp.net-identitycnet

I'm trying to implement a custom UserStore for my identity models; however, I'm getting this run-time error on app startup:

InvalidOperationException: Unable to resolve service for type
'[Project].Models.Identity.ApplicationUserStore' while attempting to
activate '[Project].Models.Identity.ApplicationUserManager'.

Stack:

Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type
serviceType, Type implementationType, CallSiteChain callSiteChain,
ParameterInfo[] parameters, bool throwIfCallSiteNotFound)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(Type
serviceType, Type implementationType, CallSiteChain callSiteChain)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor
descriptor, Type serviceType, CallSiteChain callSiteChain)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type
serviceType, CallSiteChain callSiteChain)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type
serviceType, CallSiteChain callSiteChain)
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.CreateServiceAccessor(Type
serviceType) System.Collections.Concurrent.ConcurrentDictionary.GetOrAdd(TKey key, Func valueFactory)
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type
serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type
serviceType)
Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider
provider, Type serviceType)
Microsoft.AspNetCore.Identity.IdentityBuilder+<>c__DisplayClass22_0.b__0(IServiceProvider services)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite
factoryCallSite, ServiceProviderEngineScope scope)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor.VisitCallSite(IServiceCallSite callSite, TArgument argument)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScoped(ScopedCallSite
scopedCallSite, ServiceProviderEngineScope scope)
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor.VisitCallSite(IServiceCallSite callSite, TArgument argument)
Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine+<>c__DisplayClass1_0.b__0(ServiceProviderEngineScope
scope)
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type
serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type
serviceType)
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
lambda_method(Closure , IServiceProvider , object[] )
Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider+<>c__DisplayClass4_0.b__0(ControllerContext
controllerContext)
Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider+<>c__DisplayClass5_0.g__CreateController|0(ControllerContext
controllerContext)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref
State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext
context) Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(ref
State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext
httpContext)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext
context)
Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext
context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)

My UserStore implementation:

public class ApplicationUserStore : IUserStore<Employee>,
                     IUserClaimStore<Employee>,
                     IUserLoginStore<Employee>,
                     IUserRoleStore<Employee>,
                     IUserPasswordStore<Employee>,
                     IUserSecurityStampStore<Employee>
{
    #region Constructor signatures I tried for dependency injection
    public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null)
    {

    }
    public ApplicationUserStore(ApplicationDbContext context)
    {

    }
    public ApplicationUserStore()
    {

    }
    public ApplicationUserStore(DbContext context)
    {

    }
    #endregion
    ...

My UserManager implementation:

public class ApplicationUserManager : UserManager<Employee>
{
    public ApplicationUserManager(ApplicationUserStore store, IOptions<IdentityOptions> optionsAccessor, IPasswordHasher<Employee> passwordHasher, IEnumerable<IUserValidator<Employee>> userValidators, IEnumerable<IPasswordValidator<Employee>> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<Employee>> logger)
        : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    {
    }
}

My SignInManager implementation:

public class ApplicationSignInManager : SignInManager<Employee>
{
    public ApplicationSignInManager(ApplicationUserManager userManager, IHttpContextAccessor contextAccessor, IUserClaimsPrincipalFactory<Employee> claimsFactory, IOptions<IdentityOptions> optionsAccessor, ILogger<SignInManager<Employee>> logger, IAuthenticationSchemeProvider schemes)
        : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes)
    {
    }
}

And, in Startup.ConfigureServices:

        ...
        services.AddIdentityCore<Employee>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddUserStore<ApplicationUserStore>()
            .AddUserManager<ApplicationUserManager>()
            .AddSignInManager<ApplicationSignInManager>()
            .AddDefaultTokenProviders();

        services.AddTransient<IUserStore<Employee>, ApplicationUserStore>();
        ...

I need to know what causes the problem, how to solve it, and whether there's a documentation anywhere for Asp.Net Core 2.1 dependency injection activator signatures.

Best Answer

After reviewing open-source implementation here, and here, I came up with a clue to what the issue might have been - and I guessed right. The UserStore needed to be registered through this line in Startup.ConfigureServices:

    services.AddScoped<ApplicationUserStore>();

So, my final approach was:

    ...
    services.AddIdentityCore<Employee>()
        .AddUserManager<ApplicationUserManager>()
        .AddSignInManager<ApplicationSignInManager>()
        .AddDefaultTokenProviders();

    services.AddScoped<IUserStore<Employee>, ApplicationUserStore>();
    services.AddScoped<ApplicationUserStore>();
    ...

Even though, it worked without:

    services.AddScoped<IUserStore<Employee>, ApplicationUserStore>();
Related Topic