0

I've recently stumbled on this strange error. It looks like one of my object is disposing before my method finish all async Task. But let's take a look.

I have that async method which seeds my database. It's called in Startup class in Configure method.

private void InitDatabase(IApplicationBuilder app) {
            using (var scope = app.ApplicationServices.CreateScope()) {
                var services = scope.ServiceProvider;
                try {
                    var context = services.GetRequiredService<GrabGoContext>();
                    new DatabaseInitializer().InitializeAsync(context,
                        services.GetService(typeof(UserManager<User>)) as UserManager<User>,
                        services.GetService(typeof(SignInManager<User>)) as SignInManager<User>,
                        services.GetService(typeof(RoleManager<IdentityRole<int>>)) as RoleManager<IdentityRole<int>>,
                        _logger).Wait();
                    _logger.LogDebug("Database initialization finished.");
                }
                catch (Exception ex) {
                    _logger.LogError(ex, "An error occurred while seeding the database.");
                    if (Debugger.IsAttached) Debugger.Break();
                }
            }
        }

And that method from DatabaseInitializer class:

    public async Task InitializeAsync(GrabGoContext context,
        UserManager<User> userManager,
        SignInManager<User> signInManager,
        RoleManager<IdentityRole<int>> roleManager,
        ILogger<Startup> logger) {

        if(IsDatabaseInialized) return;
        IsDatabaseInialized = true;
        await context.Database.EnsureCreatedAsync();
        await AddStores(context);
        await AddProducts(context);
        await AddRoles(roleManager)
            .ContinueWith(t => AddRolesToUsers(userManager)); // <- Here throws exception "System.ObjectDisposedException" for object `userManager`
        await AddUsers(userManager);
        await context.SaveChangesAsync();
    }

The thing is that if I use ContinueWith to chain Tasks, it throws System.ObjectDisposedException on userManager object. But I need to use ContinueWith, because I need Roles before I will assign users to that roles.

My methods responsible for adding roles

        private async Task AddRoles(RoleManager<IdentityRole<int>> roleManager) {
            if (await roleManager.Roles.AnyAsync()) return;

            var roles = (List<IdentityRole<int>>) new RolesListExample().GetExamples();
            foreach (var role in roles) {
                var result = await roleManager.CreateAsync(role);
                if(!result.Succeeded) throw new ApplicationException("Erros while adding roles: " +
                                                                     JsonConvert.SerializeObject(result.Errors));
            }
        }

        private async Task AddRolesToUsers(UserManager<User> userManager) {
            var registerModels = (List<RegisterModel>)new RegisterListRequestExample().GetExamples();
            foreach(var registerModel in registerModels) {
                var user = await userManager.FindByEmailAsync(registerModel.Email);
                var result = await userManager.AddToRoleAsync(user, registerModel.Role.ToString());
                if(!result.Succeeded)
                    throw new ApplicationException("Erros while adding roles to user: " +
                                                   JsonConvert.SerializeObject(result.Errors));
            }
        }

And also my RolesListExample class :

    public class RolesListExample : IExamplesProvider {
        /// <inheritdoc />
        public object GetExamples() {
            var roles = new List<IdentityRole<int>>();
            foreach (var availableRole in (AvailableRoles[]) Enum.GetValues(typeof(AvailableRoles))) {
                roles.Add(new IdentityRole<int>(availableRole.ToString()));
            }
            return roles;
        }
    }

My question is:

Why is my object disposing before all Tasks are finished?

I assume that's because of the using. Also I found that Question, but didn't clarify situation for me.

@Update

As suggested by @PanagiotisKanavos I removed using statement and it solved my disposing problem. Thanks!

Morasiu
  • 1,204
  • 2
  • 16
  • 38
  • You should start by simplifying your code and actually using dependency injection, instead of trying to grab service instances. The way this code is written it's little better than using `new GrabGoContext()`, `new UserManager()` etc.You don't need a scope as none of those classes is going to be used outside the method. – Panagiotis Kanavos Apr 12 '19 at 08:37
  • If you need to use those services in `DatabaseInitializer`, add them as constructor dependencies of `DatabaseInitializer` itself and create the class through DI. `InitDatabase` doesn't need to know how DatabaseInitializer is implemented, like it does now – Panagiotis Kanavos Apr 12 '19 at 08:39
  • 2
    As for chaining tasks, that's what `await` does. Why not use `await AddRoles(); await AddRolesToUsers();` ? Why use `ContinueWith()` ? – Panagiotis Kanavos Apr 12 '19 at 08:40
  • Thanks for your comments! Anyway if I use it like this I got error "System.InvalidOperationException: 'Role CLIENT does not exist.'" in `AddRoleToUsers()`. – Morasiu Apr 12 '19 at 08:44
  • Than the problem is solved, right? @Morasiu – Patrick Hofman Apr 12 '19 at 08:44
  • @Morasiu this means you have more bugs in those methods that you haven't posted. Why didn't `AddRoles` add that role when it claims it had? `ContinueWith` just moved the bug to the next call and resulted in that `ObjectDisposedException` – Panagiotis Kanavos Apr 12 '19 at 08:47
  • @PatrickHofman Still checking. – Morasiu Apr 12 '19 at 08:47
  • 1
    @Morasiu it's not possible to guess what's wrong without the code. It has nothing to do with `async` or Tasks anyway. For all one knows, one of those methods may be calling `Dispose()` on one of the contexts explicitly, or wrapping it in a `using` block. Or there could be a `catch{}` that hides errors – Panagiotis Kanavos Apr 12 '19 at 08:48
  • Nevermind. Cleaning table and removed using solved it. Thanks for help. I really appreciate it. – Morasiu Apr 12 '19 at 09:02

0 Answers0