提高实体框架查询的性能

时间:2016-04-15 15:21:11

标签: c# .net performance entity-framework

我一直在测试实体框架,试图更好地理解它,看看它如何有效地用作查询数据库的后端设备。

作为参考,我知道Entity Framework默认使用延迟加载。对于像我想要创建的那样的后端系统,这没用。

int x = 0;
using (SandboxContext dbc = new SandboxContext()) {
    var customers = (from c in dbc.Customer orderby c.AcctNumber select new { c.CustomerKey, c.AcctNumber }).ToList();
    var products = (from p in dbc.Product orderby p.CustomerKey select new { p.CustomerKey }).ToList();
    foreach (var c in customers)
        foreach (var p in products.Where(s => s.CustomerKey == c.CustomerKey))
            ++x;
    dbc.Dispose();
}
return x;

这是与我目前正在使用的代码相同的代码。

我尝试的所有内容似乎只会恶化方法的性能。作为参考,此代码在我的机器上执行约5秒钟,以返回大约22000条自动生成数据的计数。另一方面,此代码几乎立即运行以获得相同的结果:

SqlConnection sqlc = new SqlConnection(sqlConnectString);
SqlDataAdapter sqlda = new SqlDataAdapter("SELECT customerkey, acctnumber FROM customers", sqlc);

DataTable dtCustomers = new DataTable(), dtProducts = new DataTable();
sqlda.Fill(dtCustomers);
sqlda.SelectCommand.CommandText = "SELECT customerkey FROM product";
sqlda.Fill(dtProducts);
sqlda.Dispose();
sqlc.Close();

DataView dvCustomers = new DataView(dtCustomers) { Sort = "AcctNumber" };
DataView dvProducts = new DataView(dtProducts) { Sort = "CustomerKey" };

int x = 0;
for (int y = 0; y < 1000; y++)
    foreach (DataRowView drvCustomers in dvCustomers) {
        DataRowView[] drvaProducts = dvProducts.FindRows(drvCustomers["customerkey"].ToString());
        foreach (DataRowView drvProducts in drvaProducts)
            ++x;
        }
return x;

我更喜欢实体框架代码的清晰度和可读性,但我认为我错过了一些重要的信息,这些信息严重损害了我的方法的速度。是否有任何改进实体框架代码的想法至少接近DataTable / DataView / DataRowView实现的速度?

4 个答案:

答案 0 :(得分:3)

如果您的上下文设置正确,您会发现Customer的集合为Product。为了这个答案,让我们称之为Products

通过使用Products属性,您要求EF代表您进行加入,因此您不必再自己明确写入联接。事实上,写出加入“长手”是不必要的冗长,并且这将是一件非常奇怪的事情,因为EF会为你做这一切。

因此,现在,您可以像以下一样轻松选择属于客户的产品:

dbc.Customer.Select(c => c.Products)

这将有效地为您提供产品列表的列表。

现在,如果我们将列表列表变为包含SelectMany的列表,则很容易对结果列表中的产品进行计数。

所以:

using(var dbc = new SandboxContext())
{
    var customerProductsCount = dbc.Customer
                                   .SelectMany(c => c.Products)
                                   .Count();
} //note, no .Dispose... `using` took care of that for us.

答案 1 :(得分:1)

您不应在using语句中处理您的上下文,因为using会为您执行此操作。

调用ToList将执行查询并阻止您构建复杂查询并在数据库端调用它们。调用ToList将从数据库中获取数据,并可能显着降低性能。

当您不需要查询结果时,无需订购查询结果。它只会增加开销并增加执行时间。

最后,您似乎可以将整个代码简化为简单查询(thanks JoaoFSA)。

using (SandboxContext dbc = new SandboxContext())
{   
    return dbc.Customer.Join(dbc.Product,
                 c => c.CustomerKey,
                 p => p.CustomerKey,
                 (c, p) => new { Customer = c, Product = p})
              .Count();
}

答案 2 :(得分:1)

当你使用EF时,你正在对数据库进行排序,但是你正在使用的是你在代码中进行的,如果我正确地阅读它,性能可能会有所不同。

但是我在你的方法中发现的最大问题是你将所有客户和产品从数据库加载到应用程序,然后在应用程序中进行连接和计数,如果在DB中使用某些东西完成,性能会好得多像这样:

using (SandboxContext dbc = new SandboxContext()) {
   return (from c in dbc.Customer join p in dbc.Product on c.CustomerKey equals p.CustomerKey select p).Count(); }

答案 3 :(得分:1)

您是否对禁用延迟加载感兴趣?您可以通过添加

来禁用它
testVariable

在SandboxContext上下文的构造函数中。