entity framework核心数量没有最佳性能
我需要通过某个filter获取记录数量。
从理论上讲这条指令:
_dbContext.People.Count (w => w.Type == 1);
它应该生成SQL,如:
Select count (*) from People Where Type = 1
但是,生成的SQL是:
Select Id, Name, Type, DateCreated, DateLastUpdate, Address from People Where Type = 1
生成的查询在具有许多记录的数据库中运行需要更长的时间。
我需要生成第一个查询。
如果我这样做:
_dbContext.People.Count ();
entity framework生成以下查询:
Select count (*) from People
..运行得非常快。
如何生成第二个查询将搜索条件传递给计数?
这里没什么可回答的。 如果您的ORM工具没有从简单的LINQ查询生成预期的SQL查询,那么您无法通过重写查询来让它执行此操作(并且您不应该首先执行此操作)。
EF Core 在LINQ查询中具有混合客户端/数据库评估的概念,允许他们发布具有不完整/非常低效的查询处理的EF Core版本,就像您的情况一样。
不包含在EF Core中的function摘录(注意单词not )和路线图 :
改进了转换以使更多查询成功执行,在数据库(而不是内存)中评估更多逻辑。
不久,他们正在计划改进查询处理,但我们不知道何时会发生这种程度和程度(记住混合模式允许他们考虑查询“工作”)。
那有什么选择呢?
- 首先,远离EF Core,直到它变得非常有用。 回到EF6,没有这样的问题。
- 如果您无法使用EF6,请使用最新的EF Core版本保持更新。
例如,在v1.0.1和v1.1.0中,您查询生成了预期的SQL(已测试),因此您可以简单地升级并且具体问题将消失。
但请注意,随着改进,新版本会引入错误/回归(正如您在此处看到的, EFCore为简单的LEFT OUTER连接返回了太多列 ),因此请自行承担风险(并再次考虑第一个选项,即哪一个适合你 🙂
试试这个
(from x in _dbContext.People where x.Type == 1 select x).Count();
或者你可以做它的异步版本,如:
await (from x in _dbContext.People where x.Type == 1 select x).CountAsync();
如果那些不适合你,那么你至少可以通过这样做来提高查询效率:
(from x in _dbContext.People where x.Type == 1 select x.Id).Count();
要么
await (from x in _dbContext.People where x.Type == 1 select x.Id).CountAsync();
如果要优化性能并且当前的EF提供程序(尚未)能够生成所需的查询,则始终可以依赖原始SQL 。
显然,这是一个权衡,因为您使用EF来避免直接编写SQL,但如果您要执行的查询无法使用LINQ表示,或者如果使用LINQ查询导致使用原始SQL,则使用原始SQL会很有用将低效的SQL发送到数据库。
示例原始SQL查询将如下所示:
var results = _context.People.FromSql("SELECT Id, Name, Type, " + "FROM People " + "WHERE Type = @p0", 1);
据我所知,传递给FromSql
扩展方法的原始SQL查询当前要求您返回模型类型,即可能尚不支持返回标量结果。
但是,您可以始终返回到纯ADO.NET查询:
using (var connection = _context.Database.GetDbConnection()) { connection.Open(); using (var command = connection.CreateCommand()) { command.CommandText = "SELECT COUNT(*) FROM People WHERE Type = 1"; var result = command.ExecuteScalar().ToString(); } }
尝试更快地使用此lambda表达式执行查询。
_dbContext.People.select(x=> x.id).Count();
这会得到你想要的:
_dbContext.People.Where(w => w.Type == 1).Count();
似乎Entity Framework Core的早期版本之一存在一些问题。 不幸的是,你没有指定确切的版本,所以我无法深入了解EF源代码,以确定究竟出了什么问题。
为了测试这种情况,我安装了最新的EF Core软件包并设法获得正确的结果。
这是我的测试程序:
以下是SQL Server Profiler捕获的SQL生成内容:
正如您所看到的,它符合所有期望。
以下是packages.config文件的摘录:
... ...
因此,在您的情况下,唯一的解决方案是在编写本文时更新到最新的1.1.0版程序包。
我在这里使用EFCore 1.1。
如果EFCore无法将整个Where子句转换为SQL,则会发生这种情况。 这可以像DateTime.Now
那样简单,甚至可能没有想到。
以下语句导致SQL查询在加载整个表后会令人惊讶地运行SELECT *
,然后运行C# .Count()
!
int sentCount = ctx.ScheduledEmail.Where(x => x.template == template && x.SendConfirmedDate > DateTime.Now.AddDays(-7)).Count();
但是这个查询将运行SQL SELECT COUNT(*)
正如您期望/希望的那样:
DateTime earliestDate = DateTime.Now.AddDays(-7); int sentCount = ctx.ScheduledEmail.Where(x => x.template == template && x.SendConfirmedDate > earliestDate).Count();
疯狂但真实。 幸运的是,这也有效:
DateTime now = DateTime.Now; int sentCount = ctx.ScheduledEmail.Where(x => x.template == template && x.SendConfirmedDate > now.AddDays(-7)).Count();
我以前使用搜索查询计算行数是
_dbContext.People.Where(w => w.Type == 1).Count();
这也可以通过以下方式实现
List people = new List (); people = _dbContext.People.Where(w => w.Type == 1); int count = people.Count();
这样,如果您需要它,您也可以获得人员列表。