My setup: asp.net mvc web application with an attached sql database.
Given 3 sample tables such as:
Where I have to get a list of all invoices for a given contract and the total invoice sum (Sum of UnitsSold * UnitPrice) of their sold items.
I tried below controller action:
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;
}
But at var items = db.Items.Where(t => t.InvoiceId == invoice.InvoiceId);
I get: Invalid Operation Exception: This command is already assigned to an open DataReader, which must first be closed. I also tried IQueryable instead of IEnumerable, but still faulty.
Any help for getting this right would be very appreciated.
Edit and solution:
In fact the above sample is a simplified version of my problem but the given posts helped me to come up with a solution like:
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;
}
I chose the ToList() approach because it worked. The approach with the navigation property works too but there I also had use ToList() in order to be able to submit the database changes for the Invoice.Total field inside the outer loop.