【问题标题】:Async & await multiple tasks in a loop, how to return single Task循环中的异步和等待多个任务,如何返回单个任务
【发布时间】:2013-12-29 21:44:55
【问题描述】:

这个例子是实体框架,但问题更多是关于如何在一个循环中await 多个async 任务。假设我在自定义 DbContext 类上有以下方法:

public async Task DiscardChangesAsync()
{
    foreach (var entry in ChangeTracker.Entries().Where(x => x != null))
    {
        switch (entry.State)
        {
            case EntityState.Added:
                entry.State = EntityState.Detached;
                break;
            case EntityState.Modified:
                entry.State = EntityState.Unchanged;
                break;
            case EntityState.Deleted:
                await entry.ReloadAsync(); // <-- only async method
                break;
        }
    }
}

上面编译并运行,但我不确定它的效率如何。例如,如果上下文包含 10 个已删除的实体条目,则线程会在每个 ReloadAsync 处停止并等待,然后再继续循环,对吗?有什么方法可以让循环继续执行,并返回一个新的Task,直到所有 10 个ReloadAsync 调用都完成后才能完成?

【问题讨论】:

    标签: loops task-parallel-library async-await c#-5.0 entity-framework-6


    【解决方案1】:

    严格回答你的问题:

    public Task DiscardChangesAsync()
    {
        List<Task> tasks = new List<Task>();
        foreach (var entry in ChangeTracker.Entries().Where(x => x != null))
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.State = EntityState.Detached;
                    break;
                case EntityState.Modified:
                    entry.State = EntityState.Unchanged;
                    break;
                case EntityState.Deleted:
                    tasks.Add(entry.ReloadAsync());
                    break;
            }
        }
    
        return Task.WhenAll(tasks);
    }
    

    不过,让 EF 一次处理所有重载可能会更有效:

    public Task DiscardChangesAsync()
    {
        List<DbEntityEntry> deleted = new List<DbEntityEntry>();
    
        foreach (var entry in ChangeTracker.Entries().Where(x => x != null))
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.State = EntityState.Detached;
                    break;
                case EntityState.Modified:
                    entry.State = EntityState.Unchanged;
                    break;
                case EntityState.Deleted:
                    deleted.Add(entry);
                    break;
            }
        }
    
        return ctx.RefreshAsync(RefreshMode.StoreWins, deleted);
    }
    

    【讨论】:

    • 我必须将this 转换为IObjectContextAdapter 才能调用RefreshAsync。那里有什么问题吗?想知道为什么他们从 DbContext.. 中省略了该方法吗?另外,参数不应该是RefreshMode.StoreWins 而不是RefreshMode.OverwriteCurrentValues
    • 原来第二个更有效的选项在运行时失败。在将DbContext 转换为IObjectContextAdapter 并使用RefreshMode.StoreWins 对其进行编译后,我的集成测试失败并显示“要刷新的对象集合中索引0 处的元素具有空EntityKey 属性值或未附加到这个 ObjectStateManager。”不过,第一个严格的答案并没有通过集成测试。
    • 啊,我给了你一个 LINQ to SQL RefreshMode。你是对的RefreshMode.StoreWins是正确的。
    • 我之前以为我这样做是为了取消删除实体,但我想我错了。
    猜你喜欢
    • 2014-10-01
    • 2016-03-16
    • 1970-01-01
    • 2013-02-10
    • 1970-01-01
    • 2023-03-24
    • 2016-10-27
    相关资源
    最近更新 更多