【问题标题】:Seeding users in Startup file in EF core: Dependency Injection Error在 EF 核心的启动文件中播种用户:依赖注入错误
【发布时间】:2019-09-22 19:50:49
【问题描述】:

我有以下SampleData 类在运行EF Migration 后生成一些用户数据。

namespace App.Models
{
    public interface ISampleData
    {
        void Initialize();
    }
    public class SampleData: ISampleData
    {
        private readonly AppDbContext _context;
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly RoleManager<IdentityRole> _roleManager;

        public SampleData(
            AppDbContext context,
            UserManager<ApplicationUser> userManager,
            RoleManager<IdentityRole> roleManager
            )
        {
            _context = context;
            _userManager = userManager;
            _roleManager = roleManager;
        }


        public void Initialize()
        {
            ApplicationUser user;

            IdentityRole myrole = _roleManager.FindByNameAsync("Admin").Result;

            if (myrole != null) return;

            IdentityResult result = _roleManager.CreateAsync(new IdentityRole { Name = "Admin", NormalizedName = "Admin".ToUpper() }).Result;
            string userId1 = Guid.NewGuid().ToString();
            if (result.Succeeded)
            {
                user = new ApplicationUser
                {
                    Id = userId1.ToString(),
                    UserName = "erkanererkaner@gmail.com",
                    Email = "erkanererkaner@gmail.com",
                    FirstName = "Erkan",
                    LastName = "Er"
                };

                result = _userManager.CreateAsync(user, "123456Aa*").Result;
                if (result.Succeeded) _userManager.AddToRoleAsync(user, "Admin").Wait();
            }   
        }                    
    }
}

Initialize() 方法是从Startup 文件中调用的,如下所示:

public class Startup
{
    private readonly ISampleData _sampleData;
    public Startup(IConfiguration configuration, ISampleData sampleData)
    {
        _sampleData = sampleData;
        Configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        //other implementation details

        services.AddScoped<ISampleData, SampleData>(); 
    }
}
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider services)
    {
        //other implementation details

        _sampleData.Initialize();
    }

但是,我收到以下错误:

使用来自 IWebHost 访问器的应用程序服务提供程序 '程序'。 System.InvalidOperationException:无法解析服务 尝试激活时输入“App.Models.ISampleData” '应用程序启动'。在 Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider 提供者)

我看到这与我实现Dependency Injection 的方式有关。但我看不到问题所在。有什么想法吗?

【问题讨论】:

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


    【解决方案1】:

    您不能在 Startup 构造函数中注入除 IConfigurationIHostingEnvironment 之外的类型。原因是Startup类实际上是先配置了依赖注入容器(通过ConfigureServices方法)。所以当你想解决依赖关系时(在构造函数中),整个 DI 基础设施甚至还不存在。

    相反,您可以最早在 Configure 方法中解析服务,该方法在创建依赖注入容器之后被调用。您实际上可以直接在方法签名中添加依赖项以使其自动解析:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ISampleData sampleData)
    {
        // …
    
        sampleData.Initialize();
    }
    

    请注意,对于为您的数据库播种,通常不建议在 Configure 方法中执行此操作,因为这可能会在您还不想为数据库播种的情况下运行(例如,当您进行集成时)测试,或者当您从命令行调用 dotnet ef 时)。

    相反,为数据库播种的推荐模式是在启动之外但在 Web 主机级别执行此操作。所以在你的 Program 类中,你可以像这样修改它:

    public class Program
    {
        public static void Main(string[] args)
        {
            // create web host
            var host = CreateWebHostBuilder(args).Build();
    
            // create service scope for seeding the database
            using (var scope = host.Services.CreateScope())
            {
                // retrieve the sample data service
                var sampleData = scope.ServiceProvider.GetRequiredService<ISampleData>();
    
                // run your sample data initialization
                _sampleData.Initialize();
            }
    
            // run the application
            host.Run();
        }
    
        public static IWebHostBuilder CreateWebHostBuilder(string[] args)
            => WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
    

    【讨论】:

    • 采用这种方式,是否会在每次程序运行时调用_sampleData.Initialize();尝试创建相同的数据?
    • 是的,只要应用程序启动,Initialize 就会运行(就像在 Configure 方法中调用它一样)。因此,您应该构建您的服务,以便它检查是否必须为数据播种,并且仅在数据不存在时才创建它。
    猜你喜欢
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    • 2018-12-14
    • 2021-09-02
    • 1970-01-01
    • 2018-01-03
    • 2019-02-16
    • 1970-01-01
    相关资源
    最近更新 更多