Заполнение пользователей в файле запуска в ядре EF: ошибка внедрения зависимости - PullRequest
2 голосов
/ 04 мая 2019

У меня есть следующий класс 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 ]

2 голосов
/ 04 мая 2019

Вы не можете вводить типы, отличные от IConfiguration и IHostingEnvironment, в своем конструкторе запуска. Причина этого заключается в том, что класс Startup на самом деле сначала настраивает контейнер внедрения зависимостей (с помощью метода ConfigureServices). Поэтому, когда вы хотите разрешить зависимости (в конструкторе), вся инфраструктура DI еще даже не существует.

Вместо этого вы можете разрешить службы раньше всего в методе Configure, который вызывается после создания контейнера внедрения зависимости. Вы можете добавить зависимости непосредственно в сигнатуру метода, чтобы они разрешались автоматически:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ISampleData sampleData)
{
    // …

    sampleData.Initialize();
}

Обратите внимание, что для заполнения вашей базы данных, как правило, не рекомендуется делать это с помощью метода Configure, поскольку он может выполняться в ситуациях, когда вы еще не хотите заполнять базу данных (например, когда вы выполняете интеграцию). тесты, или когда вы вызываете dotnet ef из командной строки).

Вместо этого, рекомендуемый шаблон для заполнения базы данных - делать это вне запуска, но вместо этого на уровне веб-хоста. Так что внутри вашего 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 голос
/ 04 мая 2019

С помощью 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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...