Разница между foreachasync и для цикла с записями сущностей? - PullRequest
0 голосов
/ 14 мая 2019

Мне любопытно узнать, в чем разница между двумя приведенными ниже инструкциями и почему .ForEacHAsync не работает для создания новых строк, а цикл for работает?

это работает и добавляет новые продукты записи

var recordProducts = context.RecordsProducts
            .Where(i => i.RecordId == model.OldRecordId);

        foreach (var rp in recordProducts)
        {
            var newRecordProduct = new RecordProduct
            {
                IsActive = true,
                RecordId = model.RecordId,
                ProductId = rp.ProductId,
                DefendantId = rp.DefendantId,
                Annotation = rp.Annotation
            };
            context.RecordsProducts.Add(newRecordProduct);
        }

это не

var recordProducts = context.RecordsProducts
            .Where(i => i.RecordId == model.OldRecordId)
            .ForEachAsync(a =>
            {
                var newRecordProduct = new RecordProduct
                {
                    IsActive = true,
                    RecordId = model.RecordId,
                    ProductId = a.ProductId,
                    DefendantId = a.DefendantId,
                    Annotation = a.Annotation
                };
                context.RecordsProducts.Add(newRecordProduct);
            }
        );

1 Ответ

5 голосов
/ 14 мая 2019

В первом примере ваш IQueryable<RecordProduct> recordProducts будет оцениваться синхронно, а не асинхронно, поэтому он блокирует поток внутри (скрытого) вызова на IQueryable.GetEnumerator() ...MoveNext().

В то время как во втором примере метод расширения .ForEachAsync будет запускать анонимную функцию асинхронно и эквивалентен этому:

IQueryable<RecordProduct> list = await context.RecordsProducts
    .Where(i => i.RecordId == model.OldRecordId);

using( DataReader rdr = await ExecuteQueryAsDataReader( list ) )
{
    while( await rdr.ReadAsync() )
    {
        await ForEachAsyncBodyHere();
    }
}

Ваш второй пример не работает, потому что результатом вашего выражения является Task, которое никогда не будет await ed. Если вы хотите использовать ForEachAsync, вам нужно изменить код на этот:

Task loadTask = context.RecordsProducts
        .Where(i => i.RecordId == model.OldRecordId)
        .ForEachAsync(a =>
        {
            var newRecordProduct = new RecordProduct
            {
                IsActive = true,
                RecordId = model.RecordId,
                ProductId = a.ProductId,
                DefendantId = a.DefendantId,
                Annotation = a.Annotation
            };
            context.RecordsProducts.Add(newRecordProduct);
        }
    );

await loadTask; // This will wait (actually, _yield_) until all of the `ForEachAsync` iterations are complete.
await context.SaveChangesAsync(); // This will actually save the new rows added to `context.RecordsProducts`

Я не думаю, что какой-либо фрагмент кода обязательно является хорошим - я думаю, что наилучшим подходом было бы загружать все данные асинхронно одновременно, используя ToListAsync, а затем использовать обычный синхронный foreach - Add каждый запись тогда await SaveChangesAsync:

List<RecordProduct> list = await context.RecordsProducts
    .Where(i => i.RecordId == model.OldRecordId)
    .ToListAsync();

foreach( RecordProduct rp in list )
{
    context.RecordsProduct.Add( ... );
}

await context.SaveChangesAsync();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...