简体   繁体   中英

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.' <>4__this._roleManager was null

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

    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.

Additionally, services like RoleManager<TRole> are "scoped" services, meaning they can only be retrieved from a particular active scope. 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. 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. For what it's worth, this can be shortened somewhat in C# 7.2 with an actual async Main, ie:

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.

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. 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. The few situations where async void is appropriate are known to individuals who need to use it. In other words, if you don't know when you should use async void , then you shouldn't be using 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:

await UserRoleSeed.Seed(roleManager);

Finally, again, while not critical, it's convention to name async methods with an Async suffix. 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.

Ok guys I figured it out, Below is my solution.

I basically modified the class for seeding the data and renamed it 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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