Вы используете асинхронный метод для заполнения своих ролей, но не ожидаете его.Это означает, что ваш код продолжает двигаться, в конечном итоге принимая переменные, от которых вы зависите, в асинхронном методе вместе с ним, когда ветви выходят из области видимости.Следовательно, NullReferenceException
s.
Кроме того, такие сервисы, как RoleManager<TRole>
, являются сервисами с "областью действия", то есть они могут быть получены только из определенной активной области.В реальном запросе для запроса будет создана область действия, позволяющая этим службам внедряться во что-либо в конвейере запросов.Однако здесь у вас нет активной области видимости, и поэтому вы должны создать ее.
Вместо того, чтобы пытаться выполнять начальную загрузку как часть вашего Configure
метода, вы должны переместить этот код в свой класс Program
.Приведенный ниже код решает обе вышеуказанные проблемы:
public class Program
{
public static void Main(string[] args) =>
MainAsync(args).GetAwaiter().GetResult();
public static async Task MainAsync(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
await new UserRoleSeed(scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>()).Seed();
}
await host.RunAsync();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
По сути, вы будете использовать асинхронную Main для запуска вашего приложения, которая затем даст вам возможность ожидать дополнительные вещи, такие как ваше семя.Для чего это стоит, это может быть несколько сокращено в C # 7.2 с фактическим асинхронным Main, то есть:
public static async Task Main(string[] args)
Без необходимости прокси от Main
до MainAsync
,но под капотом компилятор просто настраивает эту же конструкцию для вас.
Это кратчайший путь, чтобы этот код работал, но у вас все еще есть пара незначительных проблем.Во-первых, вам следует избегать использования async void
, который является антипаттерном.По сути, вы глотаете асинхронный вывод, включая любые исключения, которые могут быть выброшены.Практически всегда следует использовать async Task
в качестве возврата, если вы не заботитесь о фактической прибыли.Те немногие ситуации, когда async void
подходит, известны лицам, которым необходимо его использовать.Другими словами, если вы не знаете, когда следует использовать async void
, то вам не следует использовать async void
.
Кроме того, хотя нет ничего технически неправильного в обновлении класса и передачезависимости в конструктор, в этом случае более уместно сделать класс статическим и передать необходимые зависимости в метод seed:
await UserRoleSeed.Seed(roleManager);
Наконец, опять же, хотя это и не критично, принято называть асинхронные методы с помощьюсуффикс Async
.Это проясняет, что метод является асинхронным, и предотвращает случайное ожидание метода просто потому, что не очевидно, что его нужно ожидать (что могло иметь место здесь).Короче говоря, измените имя с Seed
на SeedAsync
, поскольку он работает асинхронно.