2016-04-15 54 views
0

我一直在测试实体框架,以便更好地理解它,并了解它如何有效地用作查询数据库的后端设备。提高实体框架查询的性能

作为参考,我知道实体框架默认使用延迟加载。对于我试图创建的后端系统,这是没有用的。

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实现的速度?

+0

应该采取什么'x'值表示? – dotctor

+3

如果您的模型中有所有必需的互惠属性(即'Customer'包含'Product'集合),那么您尝试执行的操作应该与'dbc.Customer.SelectMany(c => c。产品).Count之间的()'。如果你的'客户'没有'产品'集合,那么考虑设置一个......没有这个,你会错过许多EF优点。 – spender

+1

首先,EF有一个预热成本,它建立了用于构建SQL的模型。 https://msdn.microsoft.com/en-us/library/bb896240(v=vs.100).aspx其次,你击中数据库两次。您可以使用未来的查询来防止这种情况发生。 https://lostechies.com/jimmybogard/2014/03/11/efficient-querying-with-linq-automapper-and-future-queries/ –

回答

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

您不应该在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(); 
} 
1

那么当您使用EF你是在做数据库排序,但目前使用的是什么,你做它的代码,如果我读它的权利,性能可能会有所不同。

但是我发现你的方法中遇到的最大问题是,你正在将所有客户和产品从数据库加载到应用程序中,然后在应用程序中进行加入和计数,如果这是在数据库中完成的,则性能会好很多像这样:

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(); } 
+0

作为一个fyi,(我不知道它是否适用于每个版本,语法对我来说还是比较新的),除非在查询块的末尾有一个select或group子句,否则该给定将不会编译尽管如此,但是)。 – LightToTheEnd

+0

是的,我在飞行中写道,忘记了。添加 – JoaoFSA

1

你有兴趣关闭延迟加载吗?您可以通过添加

this.Configuration.LazyLoadingEnabled = false; 

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