9

对于这个问题缺乏细节,我深表歉意——我需要帮助的第一件事是知道在哪里寻找更多细节。

我在提交更改时遇到了实体框架 4 导航属性的问题,显然导致性能不佳:

this.ObjectContext.SaveChanges();

当导航属性之一(收据表)包含大约 8000 行(不多,所以应该没问题)时,需要 30 多秒。

我使用了 SQL 探查器,可以看到 EF 发出 select * from Receipts 并且它非常慢:

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

目前我什至不明白为什么在调用 ObjectContext.SaveChanges() 时需要从该表中选择所有行。

它确实需要在该表中插入 1 行,但这并不能解释为什么它首先选择全部 - 也不能解释为什么该选择需要这么长时间(相同的查询在查询管理器中需要 < 1 秒)

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

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

编辑:

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

    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);
            }
        }
    }

但我仍然不明白为什么这会导致 EF 发出 select * 查询。

我认为这可能是由invoice.Receipts.Add(receipt). 因为之前那行 invoice.Receipts 是空的,并且为了 .Add 到 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);
            }
        }
    }
4

3 回答 3

1

您的问题是“仓库”实体上的“导航属性”。删除此导航属性。该关系仍然存在,但是当您创建收货实体时,它将不再查询该仓库的所有收货。我有同样的问题,这解决了我的问题。

于 2011-05-13T14:18:02.080 回答
1

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

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

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

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

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

于 2011-04-05T00:23:19.317 回答
0

你有开启延迟加载吗?如果是这样,当您访问相关的导航属性时,它将触发对WarehouseDetails和表的查询。Receipts我总是确保延迟加载被关闭,这样我就不会无意中触发查询。

于 2011-05-13T14:45:10.820 回答