169

ForEach を使用するときに Async を使用することは可能ですか? 以下は私が試しているコードです:

using (DataContext db = new DataLayer.DataContext())
{
    db.Groups.ToList().ForEach(i => async {
        await GetAdminsFromGroup(i.Gid);
    });
}

エラーが発生しています:

「Async」という名前は現在のコンテキストには存在しません

using ステートメントが囲まれているメソッドは async に設定されています。

4

10 に答える 10

241

List<T>.ForEach特にうまく機能しませんasync(同じ理由で、LINQ-to-objects もそうではありません)。

この場合、各要素を非同期操作に射影することをお勧めします。そうすれば、すべての要素が完了するまで (非同期で) 待機できます。

using (DataContext db = new DataLayer.DataContext())
{
    var tasks = db.Groups.ToList().Select(i => GetAdminsFromGroupAsync(i.Gid));
    var results = await Task.WhenAll(tasks);
}

asyncデリゲートを与えることに対するこのアプローチの利点は次のForEachとおりです。

  1. エラー処理はより適切です。からの例外は;async voidでキャッチできません。catchこのアプローチは、行で例外を伝播し、await Task.WhenAll自然な例外処理を可能にします。
  2. このメソッドはawait Task.WhenAll. を使用するasync voidと、操作がいつ完了したかを簡単に知ることができません。
  3. このアプローチには、結果を取得するための自然な構文があります。GetAdminsFromGroupAsync結果を生成する操作 (管理者) のように聞こえますが、そのようなコードは、副作用として値を設定するのではなく、そのような操作が結果を返すことができる場合、より自然になります。
于 2013-09-07T01:22:21.717 に答える
74

この小さな拡張メソッドにより、例外セーフな非同期反復が得られるはずです。

public static async Task ForEachAsync<T>(this List<T> list, Func<T, Task> func)
{
    foreach (var value in list)
    {
        await func(value);
    }
}

ラムダの戻り値の型を から に変更しているvoidためTask、例外は正しく伝播されます。これにより、実際には次のようなものを書くことができます。

await db.Groups.ToList().ForEachAsync(async i => {
    await GetAdminsFromGroup(i.Gid);
});
于 2015-03-11T20:38:59.023 に答える
30

から、C# 8.0ストリームを非同期で作成および使用できます。

    private async void button1_Click(object sender, EventArgs e)
    {
        IAsyncEnumerable<int> enumerable = GenerateSequence();

        await foreach (var i in enumerable)
        {
            Debug.WriteLine(i);
        }
    }

    public static async IAsyncEnumerable<int> GenerateSequence()
    {
        for (int i = 0; i < 20; i++)
        {
            await Task.Delay(100);
            yield return i;
        }
    }

もっと

于 2019-09-26T07:53:53.763 に答える
27

簡単な答えは、 のメソッドのforeach代わりにキーワードを使用することです。ForEach()List()

using (DataContext db = new DataLayer.DataContext())
{
    foreach(var i in db.Groups)
    {
        await GetAdminsFromGroup(i.Gid);
    }
}
于 2019-04-04T12:23:09.557 に答える
3

問題は、asyncキーワードが本文の前ではなく、ラムダの前に表示される必要があることです。

db.Groups.ToList().ForEach(async (i) => {
    await GetAdminsFromGroup(i.Gid);
});
于 2013-09-06T22:48:38.777 に答える
2

この拡張メソッドを追加

public static class ForEachAsyncExtension
{
    public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
    {
        return Task.WhenAll(from partition in Partitioner.Create(source).GetPartitions(dop) 
            select Task.Run(async delegate
            {
                using (partition)
                    while (partition.MoveNext())
                        await body(partition.Current).ConfigureAwait(false);
            }));
    }
}

そして、次のように使用します。

Task.Run(async () =>
{
    var s3 = new AmazonS3Client(Config.Instance.Aws.Credentials, Config.Instance.Aws.RegionEndpoint);
    var buckets = await s3.ListBucketsAsync();

    foreach (var s3Bucket in buckets.Buckets)
    {
        if (s3Bucket.BucketName.StartsWith("mybucket-"))
        {
            log.Information("Bucket => {BucketName}", s3Bucket.BucketName);

            ListObjectsResponse objects;
            try
            {
                objects = await s3.ListObjectsAsync(s3Bucket.BucketName);
            }
            catch
            {
                log.Error("Error getting objects. Bucket => {BucketName}", s3Bucket.BucketName);
                continue;
            }

            // ForEachAsync (4 is how many tasks you want to run in parallel)
            await objects.S3Objects.ForEachAsync(4, async s3Object =>
            {
                try
                {
                    log.Information("Bucket => {BucketName} => {Key}", s3Bucket.BucketName, s3Object.Key);
                    await s3.DeleteObjectAsync(s3Bucket.BucketName, s3Object.Key);
                }
                catch
                {
                    log.Error("Error deleting bucket {BucketName} object {Key}", s3Bucket.BucketName, s3Object.Key);
                }
            });

            try
            {
                await s3.DeleteBucketAsync(s3Bucket.BucketName);
            }
            catch
            {
                log.Error("Error deleting bucket {BucketName}", s3Bucket.BucketName);
            }
        }
    }
}).Wait();
于 2016-07-26T22:35:13.040 に答える
1

EntityFramework.Core を使用している場合は、拡張メソッド ForEachAsyncがあります。

使用例は次のようになります。

using Microsoft.EntityFrameworkCore;
using System.Threading.Tasks;

public class Example
{
    private readonly DbContext _dbContext;
    public Example(DbContext dbContext)
    {
        _dbContext = dbContext;
    }
    public async void LogicMethod()
    {
        
        await _dbContext.Set<dbTable>().ForEachAsync(async x =>
        {
            //logic
            await AsyncTask(x);
        });
    }

    public async Task<bool> AsyncTask(object x)
    {
        //other logic
        return await Task.FromResult<bool>(true);
    }
}
于 2021-08-24T12:26:07.877 に答える
-1

この目的で使用できる ForEach 関数が組み込まれたParallel クラスがあることを付け加えておきます。

于 2021-01-02T14:47:50.913 に答える