Помещение номера заказа в элементы в запросе linq

У меня есть следующий запрос Linq. TransactionData является IEnumerable.

var totalTransactions = 0;
viewModel.GroupedTransactions = transactionData
    .GroupBy(x => new { DocumentId = x.DocumentId ?? "Un Documented" })
    .Select(x => new GroupedTransaction
    {
        DocumentId = x.Key.DocumentId,
        Transactions = x.Select(y => new Transaction
        {
            Amount = y.CommitAmount,
            ActivityType = y.ActivityType,
            Number = totalTransactions++
        })
    })
    .OrderBy(x => x.DocumentId);

где я пытаюсь установить номер в записи транзакции, чтобы быть увеличенным числом. Это не работает, оставляя пробелы в цифрах.

Я также попробовал следующее после запроса.

foreach (var item in viewModel.GroupedTransactions.SelectMany(x => x.Transactions))
{
     item.Number = totalTransactions;
     totalTransactions++;
}

Это даже не обновило числовое значение. Что я делаю не так, или есть более простой способ, с аккуратным методом расширения linq?

2 ответа

Решение

Проблема в том, что вы закрываете переменную totalTransactionsВы должны создать локальную копию для использования. Проверьте Закрытие по переменной цикла, считающейся вредной для более подробного объяснения.

Примерно так должно работать:

var totalTransactions = 0;
viewModel.GroupedTransactions = transactionData
    .GroupBy(x => new { DocumentId = x.DocumentId ?? "Un Documented" })
    .Select(x => 
    {
      new GroupedTransaction()
      {
        DocumentId = x.Key.DocumentId,
        Transactions = x.Select(y => 
        {
          var currentTransactionId = totalTransactions;
          totalTransactions++;

          return new Transaction
          {
            Amount = y.CommitAmount,
            ActivityType = y.ActivityType,
            Number = currentTransactionId 
          }
        })
      }
    })
    .OrderBy(x => x.DocumentId);

Для вашего второго подхода с циклом foreach - вы фактически создаете новое перечисление с SelectMany() что вы потом просто выбросите

foreach (var item in viewModel.GroupedTransactions.SelectMany(x => x.Transactions))
{
     item.Number = totalTransactions;
     totalTransactions++;
}

Вместо этого вы должны принудительно оценить свою коллекцию, используя ToList() создать коллекцию, которую вы можете смело изменять.

var transactions = viewModel.GroupedTransactions
                            .SelectMany(x => x.Transactions)
                            .ToList();
foreach (var item in transactions)
{
     item.Number = totalTransactions;
     totalTransactions++;
}

Еще один способ думать об этом, что у вас есть две последовательности:

  1. операции
  2. индекс с автоинкрементом

И вы хотите получить одну последовательность транзакций с идентификаторами. Когда мы хотим объединить две последовательности, мы можем использовать Zip оператор:

viewModel.GroupedTransactions = transactionData     
    .GroupBy(x => new { DocumentId = x.DocumentId ?? "Un Documented" })
    .Zip(Enumerable.Range(0, int.MaxValue), (x, index) => new GroupedTransaction     
    {         
        DocumentId = x.Key.DocumentId,         
        Transactions = x.Select(y => new Transaction         
        {             
            Amount = y.CommitAmount,             
            ActivityType = y.ActivityType,             
            Number = index         
        })     
    })     
    .OrderBy(x => x.DocumentId); 

Это то, что вы имели в виду?

Zip объединяет две последовательности, пока не достигнет конца одной из последовательностей. Вот почему все хорошо, Enumberable.Range, чтобы получить гораздо больший диапазон чисел, чем нам на самом деле нужно.

Другие вопросы по тегам