简体   繁体   English

在Asp.net核心中植入IdentityRole时出错

[英]Error in Seeding IdentityRole in Asp.net core

I am having trouble seeding data into the identity role table. 我无法将数据播种到身份角色表中。 I always get the error 我总是得到错误

System.NullReferenceException: 'Object reference not set to an instance of an object.' System.NullReferenceException:'对象引用未设置为对象的实例。 <>4__this._roleManager was null <> 4__this._roleManager为空

I am not sure why this is happening and why it's not seeding data into the table.How do I fix this? 我不确定为什么会发生这种情况,为什么不将数据植入表中,我该如何解决呢? Below is my code 下面是我的代码

public class UserRoleSeed
{
    private readonly RoleManager<IdentityRole> _roleManager;
    private string[] _roleArray = { "Admin, TerminalManager, Dispatcher, Driver, Mechanic, Recruiter, MechanicManger" };

    public UserRoleSeed(RoleManager<IdentityRole> roleManager)
    {
        _roleManager = roleManager;
    }

    public async void Seed()
    {
        foreach (string index in _roleArray)
        {
            if ((await _roleManager.FindByNameAsync(index)) == null)
            {
                await _roleManager.CreateAsync(new IdentityRole { Name = index });
            }
        }         
    }
}

for my Startup.cs 对于我的Startup.cs

    public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<TransportDbContext>(options =>
           options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddMvc();  

            services.AddIdentity<ApplicationUser, IdentityRole<int>>()
                .AddEntityFrameworkStores<TransportDbContext>()
                .AddDefaultTokenProviders();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();

                //app.UseStaticFiles();

                app.UseAuthentication();
                app.UseMvc(
               routes =>
               {
                   routes.MapRoute("Default", "{controller=Home}/{action=Index}/{id?}");
               });
            }

            // seeds data into the identity role table
            new UserRoleSeed(app.ApplicationServices.GetService<RoleManager<IdentityRole>>()).Seed();
        }
    }
}

You're using an async method to seed your roles, but you're not awaiting it. 您正在使用异步方法来播种您的角色,但您没有等待它。 That means that your code keeps moving on, eventually taking variables you're depending on in your async method along with it when branches go out of scope. 这意味着您的代码会继续前进,最终在分支超出范围时在异步方法中使用您依赖的变量。 Hence, NullReferenceException s. 因此, NullReferenceException

Additionally, services like RoleManager<TRole> are "scoped" services, meaning they can only be retrieved from a particular active scope. 另外,像RoleManager<TRole>这样的服务是“作用域”服务,这意味着它们只能从特定的活动范围中检索。 In an actual request, a scope would be created for the request, allowing these services to be injected into anything within the request pipeline. 在实际的请求中,将为该请求创建一个范围,以允许将这些服务注入到请求管道中的任何内容中。 However, here, you have no active scope, and therefore must create one. 但是,这里您没有活动范围,因此必须创建一个范围。

Instead of attempting to seed as part of your Configure method, you should move this code out into your Program class. 而不是尝试作为您的Configure方法的一部分进行播种,您应该将此代码移到Program类中。 The code below addresses both of the above concerns: 下面的代码解决了以上两个问题:

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

Essentially you'll use an async Main to run your app, which then gives you the ability to await additional things like your seed. 本质上,您将使用异步Main来运行您的应用程序,然后使您能够等待诸如种子之类的其他事件。 For what it's worth, this can be shortened somewhat in C# 7.2 with an actual async Main, ie: 对于它的价值,可以使用实际的异步Main在C#7.2中将其缩短一些时间,即:

public static async Task Main(string[] args)

Without having to proxy from Main to a MainAsync , but under the hood the compiler just sets up this same construction for you. 不必从Main代理到MainAsync ,但在MainAsync ,编译器只是为您设置了相同的构造。

That's the shortest path to get this code working, but you still have a couple of minor issues. 这是使此代码正常工作的最短路径,但是您仍然有一些小问题。 First, you should avoid using async void , which is an antipattern. 首先,您应该避免使用async void ,这是一种反模式。 You're essentially swallowing the async output with that, including any exceptions that may be thrown. 您实际上是在吞下异步输出,包括可能引发的任何异常。 You should virtually always use async Task as the return when you don't care about the actual return. 当您实际上不关心实际收益时,实际上应该始终使用async Task作为收益。 The few situations where async void is appropriate are known to individuals who need to use it. 需要使用async void个人已知的少数情况是适当的。 In other words, if you don't know when you should use async void , then you shouldn't be using async void . 换句话说,如果您不知道何时应使用async void ,则不应使用async void

Also, while there's nothing technically wrong with newing up a class and passing the dependency into the constructor, it's more appropriate in this case to make the class static and pass the required dependencies into the seed method: 另外,尽管在类上新建一个并将依赖项传递给构造函数在技术上没有错,但在这种情况下,使该类静态化并将所需的依赖项传递给seed方法更合适:

await UserRoleSeed.Seed(roleManager);

Finally, again, while not critical, it's convention to name async methods with an Async suffix. 最后,同样,尽管不是很关键,但惯例是使用Async后缀来命名异步方法。 This makes it clear that the method is async and prevents accidentally not awaiting the method simply because it's not obvious that it needs to be awaited (which may have been the case here). 这清楚地表明该方法是异步的,并防止了因为不明显需要等待它而意外地不等待该方法(这里可能就是这种情况)。 In short, change the name from Seed to SeedAsync , since it does async work. 简而言之,将名称从Seed更改为SeedAsync ,因为它可以异步工作。

Ok guys I figured it out, Below is my solution. 好的,我想通了,下面是我的解决方案。

I basically modified the class for seeding the data and renamed it DbInitializer.cs 我基本上修改了用于植入数据的类,并将其重命名为DbInitializer.cs

 public class DbInitializer
{
    private static readonly string[] _roleArray = { "Admin", "Terminal Manager", "Dispatcher", "Driver", "Mechanic", "Recruiter", "Mechanic Manger" };

    public static async Task InitializeAync (TransportDbContext context, IServiceProvider serviceProvider)
    {
        var roleManager = serviceProvider.GetRequiredService<RoleManager<Role>>();


        foreach (string index in _roleArray)
        {
            if ((await roleManager.FindByNameAsync(index)) == null)
            {
                await roleManager.CreateAsync(new Role { Name = index });
            }
        }

    }
}}

then I called the function in my Program.cs file as suggested by @Chris Pratt. 然后我按照@Chris Pratt的建议在Program.cs文件中调用了该函数。

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())
        {
            var services = scope.ServiceProvider;
            var context = services.GetRequiredService<TransportDbContext>();
            await DbInitializer.InitializeAync(context, services);
        }

        await host.RunAsync();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
} }

thanks to everyone who tried to help me 感谢所有试图帮助我的人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM