我在将数据播种到标识角色表中时遇到问题。我总是收到错误
System.NullReferenceException: 'Object reference 未设置为 对象的实例。 <>4__this._roleManager 为 null
我不确定为什么会发生这种情况以及为什么它没有将数据播种到表中。我该如何解决这个问题? 下面是我的代码
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 });
}
}
}
}
为我的创业公司.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();
}
}
}
你正在使用异步方法来为角色设定种子,但你没有等待它。这意味着您的代码将继续前进,最终在分支超出范围时获取您在异步方法中依赖的变量。因此,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
,因为它确实可以异步工作。
好的,伙计们我想通了,这是我的解决方案。
我基本上修改了用于种子数据的类,并将其重命名为 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 });
}
}
}
}}
然后我按照 @Chris Pratt 的建议在我的程序.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>();
} }
感谢所有试图帮助我的人