简体   繁体   中英

ASP.Net Core 3.0 Dependency Injection ignoring Factory Methods?

I recently migrated to ASP.NET Core 3.0 and facing the DI issue while startups... they were working fine for ASP.NET Core 2.2. If I use the old WebHostBuilder in ASP.NET CORE 3.0 then I do not see the issues. Not sure is the issues are specific to new HostBuilder in Program.cs or DI has changed in 3.0.

Program.cs

public class Program
{
    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }
}

I have the UniqueRowKeyUserStore class as below. The constructor takes 4 interfaces.

public class UniqueRowKeyUserStore : UserStore<UniqueRowKeyUser>
{
    public UniqueRowKeyUserStore(IStoreMetadata storeMetadata, ILookupNormalizer dataNormalizer, IDataProtector dataProtector, ILookupSplitter<StorablePartitionRowKeys> defaultMerger) : base(storeMetadata, dataNormalizer, dataProtector, defaultMerger)
        {        }
}

In configure services, I register a scoped service with a factory function

services.AddScoped<UniqueRowKeyUserStore>(StartupService.NewUserStore);

StartupService.NewUserStore is a factory method that creates an instance of UniqueRowKeyUserStore

public static UniqueRowKeyUserStore NewUserStore(IServiceProvider arg)
{
return new UniqueRowKeyUserStore(new DefaultMetadata(), new DefaultNormalizer(), new DefaultProtecttor(), new DefaultSplitter());
}

As you can see that I am using the Factory method to create the instance so the expectation is that DI should not complain about IStoreMetadata or other interfaces which are expected in constructor.

But I get the error during startup, it does look like the DI in asp.net core 3.0 is ignoring the factory method ?

InvalidOperationException: Unable to resolve service for type 'IStoreMetadata' while attempting to activate 'UniqueRowKeyUserStore'.

I am not sure what I am doing wrong in ASP.NET Core 3.0

It may happen if something else injects that same class later (for example, AddIdentity ).

I cannot reproduce the problem you are describing and do not have the same classes in my Identity package (I guess you are using an extended one?), so I have to create my own:

        services.AddScoped<MyUserStore>(sp => new MyUserStore(new DefaultComponent2()));
        services.AddScoped<MyUserStore>(sp => new MyUserStore(new DefaultComponent()));
        services.AddScoped<MyUserStore>();

If I have only the first 2 lines, whenever I request MyUserStore , I get DefaultComponent() . However, if I add the 3rd line (and I never registered any IComponent ), I get the same error message as you described.

.AddUserStore<UniqueRowKeyUserStore>() is what is causing the problem right there.

/// <summary>
/// Adds an <see cref="IUserStore{TUser}"/> for the <see cref="UserType"/>.
/// </summary>
/// <typeparam name="TStore">The user store type.</typeparam>
/// <returns>The current <see cref="IdentityBuilder"/> instance.</returns>
public virtual IdentityBuilder AddUserStore<TStore>() where TStore : class
    => AddScoped(typeof(IUserStore<>).MakeGenericType(UserType), typeof(TStore));

Source

Note how it is added as IUserStore<>

So you are correct that it is not calling your registration of your implementation. It is looking for the abstraction.

Credits to @LukeVo and @Nkosi... the comments helped me debug the issue. I have no idea why the exact same code worked in asp.net core 2.2 but the DI in asp.net core 3.0 will complain about duplicate registration for service and ignore the factory method.

I have custom UserStore and UserRoleStore and I use Identity to register as below... I had to remove the .AddUserStore() and .AddUserRoleStore() as I am going to register my cutomer stores with factory method so that I can pass additional data (interfaces IStoreMetaData, ILookupNormalizer etc...)

services.AddIdentity<UniqueRowKeyUser, UserRole>()
                        .AddRoles<UserRole>()
                        .AddUserManager<UserManager<UniqueRowKeyUser>>()
                        .AddRoleManager<RoleManager<UserRole>>()
                        .AddSignInManager<SignInManager<UniqueRowKeyUser>>()
                        .AddErrorDescriber<TranslatedIdentityErrorDescriptor>()
                        .AddDefaultTokenProviders();

Now for registering the Factory method, the below code will not work as DI is not able to map to the store types registered by AddIdentity as explained above.

services.AddScoped<UniqueRowKeyUserStore>(StartupService.NewUserStore);
services.AddScoped<UserRoleStore>(StartupService.NewUserRoleStore);

So I had to change service registration and use IUserStore and IRoleStore with my factory methods and now its working...

 services.AddScoped<IUserStore<UniqueRowKeyUser>, UniqueRowKeyUserStore>(StartupService.NewUserStore);
 services.AddScoped<IRoleStore<UserRole>, UserRoleStore>(StartupService.NewUserRoleStore);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM