ASP.NET Core 2.0 MVC自定义数据库初始化程序类

时间:2017-09-09 14:39:06

标签: c# asp.net-core-2.0

我在自定义数据库初始化程序类中遇到以下异常:

  

发生System.ObjectDisposedException HResult = 0x80131622
  Message =无法访问已处置的对象。导致此错误的常见原因   正在处理从依赖注入解决的上下文   然后尝试在你的其他地方使用相同的上下文实例   应用。如果您正在调用Dispose(),则可能会发生这种情况   上下文,或将上下文包装在using语句中。如果你是   使用依赖注入,你应该让依赖注入   容器负责处理上下文实例。 Source = StackTrace:at   Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()at   Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()   在Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()   在   Microsoft.EntityFrameworkCore.DbContext.d__48.MoveNext()   在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
  在   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务   任务)   Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore 9.<CreateAsync>d__22.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter
1.GetResult()   在   Microsoft.AspNetCore.Identity.UserManager 1.<CreateAsync>d__73.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter
1.GetResult()   在   Microsoft.AspNetCore.Identity.UserManager 1.<CreateAsync>d__78.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter 1.GetResult()   在E:\ C#中的LlabApp.Data.DbInitializer.d__4.MoveNext()   projects \ Atenea \ LlabApp \ LlabApp \ LlabApp \ Data \ DbInitializer.cs:第58行

这是初始化程序:

public class DbInitializer : IDbInitializer
{
    private readonly ApplicationDbContext _Context;
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<ApplicationRole> _roleManager;

    public DbInitializer(
        ApplicationDbContext context,
        UserManager<ApplicationUser> userManager,
        RoleManager<ApplicationRole> roleManager)
    {
        _context = context;
        _userManager = userManager;
        _roleManager = roleManager;
    }

    public async void Initialize()
    {
        var adminUsers = _userManager.GetUsersInRoleAsync(RoleClaimValues.Admin).Result;
        if (adminUsers == null || adminUsers.Count == 0)
        {
(...)
        }
     }
}

public interface IDbInitializer
{
    void Initialize();
}

这是startup.cs,我在IDbInitializer中添加了ConfigureService的范围,然后从Configure调用它:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }


    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("ApplicationConnection")));

        services.AddIdentity<ApplicationUser, ApplicationRole>(o => {
            o.Password.RequireDigit = false;
            o.Password.RequiredLength = 4;
            o.Password.RequireLowercase = false;
            o.Password.RequireUppercase = false;
            o.Password.RequireNonAlphanumeric = false;
            o.User.RequireUniqueEmail = true;
        })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        services.AddMvc();

        // Add application services.
        services.AddTransient<IEmailSender, MessageServices>();
        //Seed database
        services.AddScoped<IDbInitializer, DbInitializer>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IDbInitializer dbInitializer)
    {
        loggerFactory.AddConsole();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();

            app.UseBrowserLink();
            app.UseDatabaseErrorPage();

            app.UseStatusCodePages("text/plain", "Status code page, status code: {0}");
        }
        else
        {
            app.UseExceptionHandler("/Error");
        }

        app.UseStaticFiles();
        app.UseAuthentication();

        dbInitializer.Initialize();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

我不知道为什么异常会说问题可能是上下文被放置在某个地方。

我还添加了

.UseDefaultServiceProvider(options =>
  options.ValidateScopes = false)

.UseStartup<Startup>()之后,以防它是相对于范围的东西,但异常是相同的。

2 个答案:

答案 0 :(得分:2)

我遇到了和你一样的问题。 您确实必须更改所有代码才能返回Task。否则,DbContext将在所有操作完成之前处理。

在dotnet core 2.0中更改了数据库初始化。我创建了一个关于这个特定主题的博客。 有关详细信息,请参阅http://www.locktar.nl/programming/net-core/seed-database-users-roles-dotnet-core-2-0-ef/

答案 1 :(得分:0)

好的,问题在于这一行:

public async void Initialize()

因为它是一个异步方法,它在内部等待,但因为它是无效的,所以ServiceProvider在到达第一个await之后处理dependecies,并在从启动时返回方法调用并继续执行时离开上下文。

因此,解决方案是在voidTaskDbInitializer更改为IDbInitializer,并在startap中转换dbInitializer参数并等待它完成:

((DbInitializer)dbInitializer).Initialize().Wait();

当然调用异步方法然后等待所有这些方法完成并不是很有用,但我不知道是否可以将Configure方法设置为异步方法...这就是解决方案我发现,因为它只是为了在开发环境中播种数据库,我很高兴。

任何进一步的建议将不胜感激。