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:
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);
}
}
}
In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr.
C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...
Quote from wikipedia: "A successor to the programming language B, C was originally developed at Bell Labs by Dennis Ritchie between 1972 and 1973 to construct utilities running on Unix." The creators want that everyone "see" his language. So he named it "C".
It was mainly developed as a system programming language to write an operating system. The main features of the C language include low-level memory access, a simple set of keywords, and a clean style, these features make C language suitable for system programmings like an operating system or compiler development.
Since this is an insert, it refreshes your object, by selecting the value back and repopulating the object. Now let me answer your questions that you laid out:
You shouldn't need to debug instead of SaveChanges()
, what you see probably wouldn't make much sense anyways.
It is not actually doing a select * from Receipts
. It is doing a select * from Receipts where WarehouseId = 1
. So for some reasons you object is pulling all the Receipts for the Warehouse with the Id of 1.
This could depend on so many things, that you really can't get into it now. But one place to start is to check the ping rate between your app box and your db box. Also check that the RAM isn't full on the db box. That is where I would start, and that is the usual problem for what you are describing.
A good tool to debug EF is the EF Profiler. http://efprof.com This will help you alot more than SQL profiler will.
You issue is with the "Navigation Property" on your "Warehouse" entity. Remove this navigation property. The relationship will still be there, but it will not query all receipts with that warehouse anymore when you create a receipt entity. I had the same issue and this solved my issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With