提高实体框架查询的性能
我一直在测试实体框架,以便更好地理解它,并了解它如何有效地用作查询数据库的后端设备。提高实体框架查询的性能
作为参考,我知道实体框架默认使用延迟加载。对于我试图创建的后端系统,这是没有用的。
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实现的速度?
如果您的上下文设置正确,您应该会发现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.
您不应该在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();
}
那么当您使用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(); }
作为一个fyi,(我不知道它是否适用于每个版本,语法对我来说还是比较新的),除非在查询块的末尾有一个select或group子句,否则该给定将不会编译尽管如此,但是)。 – LightToTheEnd
是的,我在飞行中写道,忘记了。添加 – JoaoFSA
你有兴趣关闭延迟加载吗?您可以通过添加
this.Configuration.LazyLoadingEnabled = false;
在您的SandboxContext上下文的构造函数中。
应该采取什么'x'值表示? – dotctor
如果您的模型中有所有必需的互惠属性(即'Customer'包含'Product'集合),那么您尝试执行的操作应该与'dbc.Customer.SelectMany(c => c。产品).Count之间的()'。如果你的'客户'没有'产品'集合,那么考虑设置一个......没有这个,你会错过许多EF优点。 – spender
首先,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/ –