0

I wrote a class to seed the database with data. When debugging it it stacks on await IdentitySeedData.EnsurePopulated(service); Probably the problem is with usermanager.createasync(). Take a look:

namespace SportStore1
{
public class Program
{
    public static void Main(string[] args)
    {
        var Host = BuildWebHost(args);
        var Scopes = Host.Services.GetRequiredService<IServiceScopeFactory>();
        using (var scope = Scopes.CreateScope())
        {
            var service = scope.ServiceProvider;
            SeedDataFunc(service);
            SeedIdentityDataFunc(service);
        }
        Host.Run();
    }

    public static void SeedDataFunc(IServiceProvider service)
    {
        SeedData.EnsurePopulated(service);
    }

    public static async void SeedIdentityDataFunc(IServiceProvider service)
    {
        await IdentitySeedData.EnsurePopulated(service);
    }

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();

    /*
    public static void Main(string[] args)
    {
        CreateWebHostBuilder(args).Build().Run();
    }

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

namespace SportStore1.Data
{
  public static class IdentitySeedData
  {
    public static async Task EnsurePopulated(IServiceProvider service)
    {
        UserManager<IdentityUser> userManager = service.GetRequiredService<UserManager<IdentityUser>>();
        IConfiguration configuration = service.GetRequiredService<IConfiguration>();
        ApplicationDbContext dbContext = service.GetRequiredService<ApplicationDbContext>();

        if (!dbContext.Roles.Any())
        {
            foreach (var role in Roles)
            {
                dbContext.Add(role);
                await dbContext.SaveChangesAsync();
            }
        }

        if (!dbContext.Users.Any())
        {
            var user = new IdentityUser() { UserName = configuration["Email"], Email = configuration["Email"], EmailConfirmed = true };
            await userManager.CreateAsync(user, configuration["Password"]);
        }

        if (!dbContext.UserRoles.Any())
        {
            var roleID = dbContext.Roles.Where(p => p.Name == "Administrator").FirstOrDefault().Id;
            var userID = dbContext.Users.Where(p => p.Email == configuration["Email"]).FirstOrDefault().Id;

            var userRole = new IdentityUserRole<string>()
            {
                RoleId = roleID,
                UserId = userID
            };

            dbContext.UserRoles.Add(userRole);
            await dbContext.SaveChangesAsync();
        }
    }

    private static List<IdentityRole> Roles = new List<IdentityRole>()
    {
         new IdentityRole { Name = "Administrator", NormalizedName = "Administrator", ConcurrencyStamp = Guid.NewGuid().ToString() },
         new IdentityRole { Name = "Manager", NormalizedName = "Manager", ConcurrencyStamp = Guid.NewGuid().ToString() },
         new IdentityRole { Name = "User", NormalizedName = "User", ConcurrencyStamp = Guid.NewGuid().ToString()}
    };
  }
 }

It stacks on await IdentitySeedData.EnsurePopulated(service) with a message System.ObjectDisposedException: 'Cannot access a disposed object. Object name: 'UserManager`1'.' Any solution?

Konstantinos
  • 19
  • 1
  • 1
  • 3

2 Answers2

0

You need to await the async method. It seems you're getting stuck on the fact that Main is synchronous, but you can change that. In C# 7.2+ you can actually just have an async main:

public static async Task Main(string[] args)

In lesser versions, you'd just do:

public static void Main(string[] args) =>
    MainAsync(args).GetAwaiter().GetResult();

public static async Task MainAsync(string[] args)
{
    ...
}

The compiler just builds that for you under the hood when you use async Main, anyways. Remember that you'll need to also do await host.RunAsync();, if you do this.

That said, this is not the method for doing database seeding any more. See the documentation.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
0

You also need to use using() statement to ensure that the dbcontext is disposed as soon as it goes out of scope.

I tried your code in a brand new asp.net core 2.0 Individual User Account which uses ApplicationUser and it works well after using using block:

public static class IdentitySeedData
{
    public static async Task EnsurePopulated(IServiceProvider service)
    {
        UserManager<ApplicationUser> userManager = service.GetRequiredService<UserManager<ApplicationUser>>();
        IConfiguration configuration = service.GetRequiredService<IConfiguration>();

        using (var dbContext = service.GetRequiredService<ApplicationDbContext>())
        {

            //ApplicationDbContext dbContext = service.GetRequiredService<ApplicationDbContext>();

            if (!dbContext.Roles.Any())
            {
                foreach (var role in Roles)
                {
                    dbContext.Add(role);
                    await dbContext.SaveChangesAsync();
                }
            }

            if (!dbContext.Users.Any())
            {
                var user = new ApplicationUser() { UserName = configuration["Email"], Email = configuration["Email"], EmailConfirmed = true };
                await userManager.CreateAsync(user, configuration["Password"]);
            }

            if (!dbContext.UserRoles.Any())
            {
                var roleID = dbContext.Roles.Where(p => p.Name == "Administrator").FirstOrDefault().Id;
                var userID = dbContext.Users.Where(p => p.Email == configuration["Email"]).FirstOrDefault().Id;

                var userRole = new IdentityUserRole<string>()
                {
                    RoleId = roleID,
                    UserId = userID
                };

                dbContext.UserRoles.Add(userRole);
                await dbContext.SaveChangesAsync();
            }
        }
    }

    private static List<IdentityRole> Roles = new List<IdentityRole>()
    {
         new IdentityRole { Name = "Administrator", NormalizedName = "Administrator", ConcurrencyStamp = Guid.NewGuid().ToString() },
         new IdentityRole { Name = "Manager", NormalizedName = "Manager", ConcurrencyStamp = Guid.NewGuid().ToString() },
         new IdentityRole { Name = "User", NormalizedName = "User", ConcurrencyStamp = Guid.NewGuid().ToString()}
    };
}

Startup:

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

        services.AddIdentity<ApplicationUser, IdentityRole>()               
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();


        services.AddTransient<IEmailSender, EmailSender>();

        services.AddMvc();
    }

Refer to Cannot access a disposed object in ASP.NET Core when injecting DbContext

Ryan
  • 19,118
  • 10
  • 37
  • 53