【问题标题】:Why is Dispose called before its finishes async task?为什么在完成异步任务之前调用 Dispose?
【发布时间】:2019-04-12 08:27:12
【问题描述】:

我最近偶然发现了这个奇怪的错误。看起来我的一个对象在我的方法完成所有异步任务之前正在处理。但是让我们看一下。

我有那个为我的数据库播种的异步方法。它在Configure 方法中的Startup 类中调用。

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();
                }
            }
        }

还有来自DatabaseInitializer 类的那个方法:

    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();
    }

问题是,如果我使用ContinueWith 链接任务,它会在userManager 对象上抛出System.ObjectDisposedException。但我需要使用ContinueWith,因为我需要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));
            }
        }

还有我的RolesListExample 班级:

    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;
        }
    }

我的问题是:

为什么我的对象在所有任务完成之前就被释放了?

我认为这是因为using。 我也发现了Question,但没有为我澄清情况。

@更新

根据@PanagiotisKanavos 的建议,我删除了using 声明,它解决了我的处置问题。谢谢!

【问题讨论】:

  • 您应该从简化代码开始并实际使用依赖注入,而不是尝试获取服务实例。这段代码的编写方式比使用new GrabGoContext()new UserManager() 等要好一点。您不需要范围,因为这些类都不会在方法之外使用。
  • 如果您需要在DatabaseInitializer 中使用这些服务,请将它们添加为DatabaseInitializer 本身的构造函数依赖项,并通过DI 创建类。 InitDatabase 不需要像现在一样知道 DatabaseInitializer 是如何实现的
  • 至于链接任务,await 就是这样做的。为什么不使用 await AddRoles(); await AddRolesToUsers(); ?为什么使用ContinueWith()
  • 感谢您的 cmets!无论如何,如果我像这样使用它,我会在AddRoleToUsers() 中收到错误“System.InvalidOperationException: 'Role CLIENT does not exist.'”。
  • @Morasiu 没有代码就无法猜测出什么问题。无论如何,它与async 或任务无关。众所周知,其中一种方法可能是在其中一个上下文上显式调用Dispose(),或者将其包装在using 块中。或者可能有一个隐藏错误的catch{}

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


【解决方案1】:

您只是在等待任务的继续。而不是:

await AddRoles(roleManager).ContinueWith(t => AddRolesToUsers(userManager));

...试试:

var task1 = AddRoles(roleManager);
var task2 = task1.ContinueWith(t => AddRolesToUsers(userManager));
await task1;
await task2;

【讨论】:

  • 感谢您的回答。不幸的是,这并没有解决我的问题。
  • 现在这两个等待抛出?正在等待 task1 还是 task2?
  • task2 抛出错误System.InvalidOperationException: 'Role CLIENT does not exist.
  • 没关系。清洁表并删除using 解决了它。感谢帮助。我真的很感激。
  • 很乐意帮助好友。我不会拒绝重击。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-16
  • 1970-01-01
  • 2017-08-07
  • 1970-01-01
  • 2020-07-10
  • 1970-01-01
  • 2020-10-31
相关资源
最近更新 更多