Как l oop через 2 вложенных набора записей без открытой ошибки DataReader? - PullRequest
1 голос
/ 31 марта 2020

Моя настройка: asp. net mvc веб-приложение с присоединенной базой данных sql.

Приведено 3 примера таблиц, таких как:

enter image description here

Где мне нужно получить список всех счетов-фактур по данному контракту и общую сумму счетов-фактур (Сумма единиц проданных * Единица измерения) их проданных товаров.

Я попробовал ниже действие контроллера:

    public IEnumerable<Invoice> Get(int contractId)
    {
        IEnumerable<Invoice> invoices = db.Invoices.Where(i => i.ContractId == invoiceId);

        foreach (var invoice in invoices){
            var items = db.Items.Where(t => t.InvoiceId == invoice.InvoiceId);
            foreach (var item in items){
                   invoice.Total += item.UnitsSold * item.UnitPrice;
            }

        }
        return invoices;
    }

Но при var items = db.Items.Where(t => t.InvoiceId == invoice.InvoiceId);

Я получаю: Недопустимая операция Исключение: эта команда уже назначена открытому DataReader, который сначала должен быть закрыт . Я также попробовал IQueryable вместо IEnumerable, но все еще неисправен.

Любая помощь для получения этого права будет очень признательна.

Редактирование и решение:

Фактически приведенный выше пример является упрощенной версией моей проблемы, но данные посты помогли мне найти решение, подобное:

    public IEnumerable<ContractInvoiceViewModel> Get(int contractId)
    {
        // here the total product sum for all invoices is built and stored in the respective invoice field
        var invoices = db.Invoices.Where(i => i.ContractId == contractId).ToList();
        foreach (var invoice in invoices)
        {
            var existingInvoice = db.Invoices.Find(invoice.InvoiceId);
            var items = db.Items.Where(t => t.InvoiceId == invoice.InvoiceId).ToList();
            decimal? tempSum = 0.00m;
            foreach (var item in items)
            {
                tempSum += item.UnitPrice * item.UnitsSold;
            }
            existingInvoice.Total = tempSum;
            db.Entry(existingInvoice).State = EntityState.Modified;
            db.SaveChanges();
        }

        //  here the viewmodels for the view are collected
        IEnumerable<Invoice> invoicesForView = db.Invoices.Where(i => i.ContractId == contractId);
        var contract = db.Contracts.Find(contractId);
        var customer = db.Customers.Find(contract.CustomerId);

        IList<ContractInvoiceViewModel> result = new List<ContractInvoiceViewModel>();
        foreach (var invoiceItem in invoicesForView)
        {
            var model = new ContractInvoiceViewModel
            {
                InvoiceId = invoiceItem.InvoiceId,
                ContractId = invoiceItem.ContractId,
                ContractDate = contract.ContractDate,
                InvoiceDate = invoiceItem.InvoiceDate,
                Customer = customer.Name,
                Info = contract.Info,
                Total = invoiceItem.Total,
            };
            result.Add(model);
        }
        return result;
    }

Я выбрал подход ToList (), потому что он работал. Подход со свойством навигации тоже работает, но там я также использовал ToList (), чтобы иметь возможность отправлять изменения базы данных для поля Invoice.Total внутри внешнего l oop.

Ответы [ 2 ]

2 голосов
/ 31 марта 2020

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.tolist?view=netframework-4.8#System_Linq_Enumerable_ToList__1_System_Collections_Generic_IEnumerable___0__

Из самой документации;

Метод ToList (IEnumerable) вызывает немедленную оценку запроса и возвращает список, содержащий запрос Результаты. Вы можете добавить этот метод к вашему запросу, чтобы получить кэшированную копию результатов запроса.

Операции над IEnumerable, который подключен к источнику данных, должны быть сначала переданы в память / кэш до редактирования свойства.

public List<Invoice> Get(int contractId)
{
   List<Invoice> invoices = db.Invoices.Where(i => i.ContractId == invoiceId).ToList();

   foreach (var invoice in invoices){
      var items = db.Items.Where(t => t.InvoiceId == invoice.InvoiceId).ToList();
      foreach (var item in items){
         invoice.Total += item.UnitsBought * item.UnitPrice;
      }
   }
   return invoices;
}
1 голос
/ 31 марта 2020

Это происходит потому, что items.GetEnumerator () должен выполнить запрос SQL и начать получать результаты, но счета находятся в середине чтения результата из базы данных. И вы (обычно) не можете запускать два разных запроса одновременно на одном и том же соединении с базой данных. Результаты первого запроса необходимо прочитать до конца, прежде чем можно будет выполнить новый запрос. На этом этапе изменения происходят только в памяти и будут позже сохранены в базе данных с вызовом db.SaveChanges (). Я понимаю, что этот ответ взят из комментариев @ DavidBrowne-Microsoft и частично из ответа @JerdineSabio. Я просто почувствовал, что вопрос требует ответа и всей необходимой информации для консолидации.

Вариант 1: используйте ToList () для загрузки запроса в память

public List<Invoice> Get(int contractId)
{
   List<Invoice> invoices = db.Invoices.Where(i => i.ContractId == invoiceId).ToList();

   foreach (var invoice in invoices){
      var items = db.Items.Where(t => t.InvoiceId == invoice.InvoiceId).ToList();
      foreach (var item in items){
         invoice.Total += item.UnitsBought * item.UnitPrice;
      }
   }
   return invoices;
}

Вариант 2: (рекомендуется ) Включите элементы, необходимые в начальном запросе. (при условии, что у вас есть свойство навигации в счете)

public IEnumerable<Invoice> Get(int contractId)
{
    IEnumerable<Invoice> invoices = db.Invoices.Include(i => i.Items).Where(i => i.ContractId == invoiceId);

    foreach (var invoice in invoices){
        var items = invoice.Items;
        foreach (var item in items){
               invoice.Total += item.UnitsBought * item.UnitPrice;
        }

    }
    return invoices;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...