I apologize for the lack of detail in this question - the first thing I need help on is knowing where to look to find more detail.
I have a problem with enity framework 4 navigation properties apparently causing poor performance when committing changes:
this.ObjectContext.SaveChanges();
Is taking 30+ seconds when one of the navigation properties (Receipts table) contains around 8000 rows (which is not many, so should be fine).
I have used the SQL profiler and can see that EF issues a select * from Receipts and that it is very slow:
exec sp_executesql N'SELECT
[Extent1].[Id] AS [Id],
// full field list cut for brevity
FROM [dbo].[Receipts] AS [Extent1]
WHERE [Extent1].[WarehouseId] = @EntityKeyValue1',
N'@EntityKeyValue1 int',@EntityKeyValue1=1
At the moment I can't even see why it needs to select all rows from this table when ObjectContext.SaveChanges() is called.
It does need to insert 1 row into this table, but that doesn't explain why it does a select all first - and doesn't explain why that select takes so long (the same query takes < 1 second in query manager)
So my question right now - I don't exactly know what the problem is yet - is:
- Where/how can I look for more details about the problem? I can't debug into ObjectContext.SaveChanges(), so I don't know whats happening inside it.
- Why would EF try to select * from Receipts?
- Why is it so slow? The exact same query copied + pasted into query manager is nearly instant
EDIT:
I have confirmed that it is the receipt code that is slow by commenting out the call to this method:
private void AddReceipt(PurchaseInvoice invoice,
PurchaseInvoiceLine invoiceLine)
{
if (invoice != null && invoiceLine != null)
{
Product product = invoiceLine.Product;
if (product != null)
{
Receipt receipt = new Receipt{ foo = bar };
WarehouseDetail detail = new WarehouseDetail{ foo = bar };
receipt.WarehouseDetails.Add(detail);
invoice.Receipts.Add(receipt);
}
}
}
But I still cannot see why this causes EF to issue that select * query.
I believe that it might be a lazy loading issue caused by invoice.Receipts.Add(receipt)
. Because before that line invoice.Receipts is empty, and in order to .Add to the Receipts, it must first load the collection. BUT that does not explain why it is selecting by warehouseId=1, when it should be selecting by the invoiceId.
EDIT 2:
I have "fixed" the problem by replacing the EF code in this method with direct SQL commands. This is not a great idea - I should not be throwing SQL around when I've got an otherwise perfectly good ORM. But right now I still do not understand why EF was running the select * query
private void AddReceipt(PurchaseInvoice invoice,
PurchaseInvoiceLine invoiceLine)
{
if (invoice != null && invoiceLine != null)
{
Product product = invoiceLine.Product;
if (product != null)
{
Receipt receipt = new Receipt{ foo = bar };
WarehouseDetail detail = new WarehouseDetail{ foo = bar };
int id = SqlHelper.AddWarehouseDetail(detail);
receipt.WarehouseDetailId = id;
SqlHelper.AddReceipt(receipt);
}
}
}