C#Entity Framework 4导航属性导致提交性能降低

JK.*_*JK. 9 c# sql-server performance entity-framework objectcontext

我为这个问题缺乏细节而道歉 - 我需要帮助的第一件事就是知道在哪里寻找更多细节.

我有一个enity框架4导航属性的问题,显然在提交更改时导致性能不佳:

this.ObjectContext.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

当其中一个导航属性(收据表)包含大约8000行(这不是很多,所以应该没问题)时,需要30多秒.

我使用过SQL分析器,可以看到EF从Receipts发出select*并且它非常慢:

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
Run Code Online (Sandbox Code Playgroud)

目前,当调用ObjectContext.SaveChanges()时,我甚至无法理解为什么需要从该表中选择所有行.

它确实需要在此表中插入1行,但这并不能解释为什么它首先执行select - 并且不能解释为什么select会花费这么长时间(同一查询在查询管理器中占用<1秒)

所以我现在的问题 - 我还不知道问题是什么 - 是:

  • 我在哪里/如何查找有关该问题的更多详细信息?我无法调试到ObjectContext.SaveChanges(),所以我不知道它内部发生了什么.
  • 为什么EF会尝试从收据中选择*?
  • 为什么这么慢?复制+粘贴到查询管理器的完全相同的查询几乎是即时的

编辑:

我已经通过注释掉对此方法的调用来确认收据代码很慢:

    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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

但我仍然无法理解为什么这会导致EF发出select*查询.

我相信它可能是由于引起的延迟加载问题invoice.Receipts.Add(receipt).因为在该行之前invoice.Receipts为空,并且为了添加到收据,它必须首先加载集合.但这并不能解释为什么它应该由warehouseId = 1选择,何时应该由invoiceId选择.

编辑2:

我通过使用直接SQL命令替换此方法中的EF代码来"修复"该问题.这不是一个好主意 - 当我有一个非常好的ORM时,我不应该抛出SQL.但是现在我仍然不明白为什么EF运行select*查询

    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);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

Nic*_*rdi 1

由于这是一个插入,因此它会通过选择回值并重新填充对象来刷新您的对象。现在让我回答您提出的问题:

  1. 您不应该需要调试而不是SaveChanges(),您所看到的可能无论如何都没有多大意义。

  2. 它实际上并不是在做一个select * from Receipts. 它正在做一个select * from Receipts where WarehouseId = 1. 因此,由于某些原因,您反对提取 ID 为 1 的仓库的所有收据。

  3. 这可能取决于很多事情,你现在确实无法介入。但一个开始的地方是检查应用程序框和数据库框之间的 ping 速率。还要检查数据库盒上的 RAM 是否已满。这就是我要开始的地方,这也是您所描述的常见问题。

EF Profiler 是调试 EF 的一个好工具。 http://efprof.com 这将比 SQL Profiler 对您有更多帮助。