【问题标题】:ASP.Net Core 3.0 Dependency Injection ignoring Factory Methods?ASP.Net Core 3.0 依赖注入忽略工厂方法?
【发布时间】:2020-03-18 02:02:45
【问题描述】:

我最近迁移到 ASP.NET Core 3.0 并在启动时遇到了 DI 问题……他们在 ASP.NET Core 2.2 上运行良好。如果我在 ASP.NET CORE 3.0 中使用旧的 WebHostBuilder,那么我看不到这些问题。不确定这些问题是特定于 Program.cs 中的新 HostBuilder 还是 3.0 中的 DI 已更改。

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();
    }
}

我有如下的 UniqueRowKeyUserStore 类。构造函数采用 4 个接口。

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

在配置服务中,我使用工厂函数注册了一个作用域服务

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

StartupService.NewUserStore 是创建 UniqueRowKeyUserStore 实例的工厂方法

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

如您所见,我使用 Factory 方法来创建实例,因此期望 DI 不应抱怨 IStoreMetadata 或构造函数中预期的其他接口。

但我在启动过程中遇到错误,看起来 asp.net core 3.0 中的 DI 忽略了工厂方法

InvalidOperationException:尝试激活“UniqueRowKeyUserStore”时无法解析“IStoreMetadata”类型的服务。

我不确定我在 ASP.NET Core 3.0 中做错了什么

【问题讨论】:

  • 您是否尝试过手动进行工厂以确保? services.AddScoped&lt;UniqueRowKeyUserStore&gt;(sp =&gt; return new UniqueRowKeyUserStore(...));
  • 我无法重现该问题。首先,我的Identity的UserStore没有这样的构造函数,也没有IStoreMetadataILookupSplitter,所以我不确定你用的是哪一个。其次,如果都正确的话,VS应该会告诉你可以在AddScoped处去掉&lt;UniqueRowKeyUserStore&gt;吧?
  • @Nkosi 是的,我按照您的建议手动尝试了工厂,但同样的问题......
  • .AddUserStore&lt;UniqueRowKeyUserStore&gt;() 是导致问题的原因。
  • @Ameya 我找到了源代码,看看为什么它会导致你的问题

标签: c# asp.net-mvc asp.net-core dependency-injection


【解决方案1】:

如果以后有其他东西注入同一个类(例如,AddIdentity),可能会发生这种情况。

我无法重现您描述的问题,并且我的 Identity 包中没有相同的类(我猜您使用的是扩展类?),所以我必须创建自己的:

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

如果我只有前 2 行,那么每当我请求 MyUserStore 时,我都会得到 DefaultComponent()。但是,如果我添加第 3 行(并且我从未注册过任何 IComponent),我会收到与您描述的相同的错误消息。

【讨论】:

  • 好的,感谢您的指点...是的,我正在使用扩展的用户存储...让我检查是否还有其他注入...但令人惊讶的是,在核心中工作的完全相同的代码2.2 我刚刚迁移到 3.0 并且启动停止工作
【解决方案2】:

感谢@LukeVo 和@Nkosi... cmets 帮助我调试了这个问题。我不知道为什么完全相同的代码在 asp.net core 2.2 中工作,但在 asp.net core 3.0 中的 DI 会抱怨重复注册服务并忽略工厂方法。

我有自定义 UserStore 和 UserRoleStore 并且我使用 Identity 注册如下...我必须删除 .AddUserStore().AddUserRoleStore() 因为我我将使用工厂方法注册我的客户商店,以便我可以传递其他数据(接口 IStoreMetaData、ILookupNormalizer 等...)

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

现在注册 Factory 方法,下面的代码将不起作用,因为 DI 无法映射到 AddIdentity 注册的商店类型,如上所述。

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

所以我不得不更改服务注册并使用 IUserStore 和 IRoleStore 与我的工厂方法,现在它的工作......

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

【讨论】:

    【解决方案3】:

    .AddUserStore&lt;UniqueRowKeyUserStore&gt;() 是造成问题的原因。

    /// <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

    注意它是如何添加为IUserStore&lt;&gt;

    所以你是正确的,它没有调用你的实现注册。它正在寻找抽象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-05
      • 2012-08-09
      • 1970-01-01
      • 2018-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多