在EF核心的启动文件中播种用户:依赖项注入错误

时间:2019-05-04 15:01:53

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

我有以下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”类型   'App.Startup'。在   Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider   提供者)

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

2 个答案:

答案 0 :(得分:2)

您不能在启动构造函数中注入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>();
}

答案 1 :(得分:1)

使用ASP.NET Core 2+,您可以通过在Startup解析之前使用ConfigureServices上的IWebHostBuilder方法向您的Startup类中注入自定义类型来注册服务及其依赖性。

唯一的警告是确保您的类型也已经注册的所有依赖项,在这种情况下,因为这些依赖项是通过AddMvc方法添加的,因此不太可能。

这只是表明它可以完成。

具有以下自定义类型

public interface IMyCustomType
{
    void DoSomethingCustom();
}

public class MyCustomType : IMyCustomType
{
    public void DoSomethingCustom()
    {
        throw new Exception("Custom stuff happens here");
    }
}

您可以在Program实例化和配置的WebHostBuilder类中进行注册。

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

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
        .ConfigureServices(services => services.AddScoped<IMyCustomType, MyCustomType>())
            .UseStartup<Startup>();
}

在此之后,您的Startup与现在的情况相同或相似-除非您不在此处的ConfigureServices中进行注册。

public class Startup
{
    private readonly IMyCustomType myCustomType;
    public Startup(IConfiguration configuration, IMyCustomType myCustomType)
    {
        Configuration = configuration;
        this.myCustomType = myCustomType;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        myCustomType.DoSomethingCustom();

        app.UseMvc();
    }
}