前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Entity Framework 的一些性能建议

Entity Framework 的一些性能建议

作者头像
Edi Wang
发布于 2019-07-09 02:00:36
发布于 2019-07-09 02:00:36
1.7K0
举报
文章被收录于专栏:汪宇杰博客汪宇杰博客

这是一篇我在2012年写的老文章,至今适用(没错,我说的就是适用于EF Core)。因此使用微信重新推送,希望能帮到大家。

自从我用了EF,每次都很关心是否有潜在的性能问题。所以每次我写LINQ查询,都会使用SQL Profiler看一下实际生成的SQL语句,以便发现潜在的性能问题。也强烈建议大家这么去做,以免日后软件大了出了问题很难查。

只选择某列或某些列

有些时候,在C#里写LINQ虽然看着舒服,但性能不一定好,所以有必要做一些调整。比如这种情况:

我需要知道一篇文章的点击数,仅此而已,我可能会写:

context.Post.FirstOrDefault(p => p.Id == postId).Hits;

或者:

context.Post.Find(postId).Hits;

我期待着他们只去数据库里筛选Hits这一列的数据,然而,通过SQL Profiler会发现,这两条语句居然把全部列都给select出来了,访问Hits的操作实际是在内存中进行的

虽然小表看不出性能问题,但万一你的表里有一列是存文件字节流(byte)的,那这样的操作可能会很慢,并且消耗额外的网络传输,所以不能忽视这个问题。

其实,我只要稍作调整,就能避免这个问题,但会LINQ语句难看一点:

context.Post.Where(p => p.Id == postId).Select(p => p.Hits).FirstOrDefault();

最终生成的native sql是这样的:

exec sp_executesql N'SELECT TOP (1) [Extent1].[Hits] AS [Hits]FROM [dbo].[Post] AS [Extent1]WHERE [Extent1].[Id] = @p__linq__0',N'@p__linq__0 uniqueidentifier',@p__linq__0='850C3A86-6C3D-408B-8099-61EDA559F804'

真正的只select了Hits一个字段。

ToList()的问题

其实EF很多时候的性能问题都是关系到查询执行时机的。我们通常的意图是,首先建立一个查询表达式,只是build,而不execute。执行的时机是用到这个表达式结果的时候才去执行。

在公司码程序的时候,我看到好多同事用EF,写完查询喜欢直接调用ToList()方法。有时候这会造成很大的性能问题。因为单纯声明一个linq表达式并不会立即执行SQL查询,然而一旦在后面加上ToList(),就会立即去执行。如果你只是想根据条件选择其中一些数据,而非全部的话,那ToList()以后再筛选,就是从内存里执行了,并不是把你的条件转换成sql的where语句去执行。

var query = from ..... // 建立查询,但不执行

var result = query.ToList(); // 立即执行查询

所以,你应当尽量避免从ToList()后的结果中再去查找自己想要的元素

IQueryable, IEnumerable

在这两个接口的选择上,我偏向使用IQueryable。大部分时候这两个接口在使用上的表现都是一致的,但如果你要做的是一个不确定的查询,意思是这个查询表达式不是一次性确定的,对于它的结果可能由别的类来选择到底select哪些东西,这时候就要用IQueryable

比如我有一个数据层方法:

public IEnumerable<EdiBlog.Core.Entities.Post> GetAllPost()

{

return context.Post;

}

很显然,它会被系统中的其他方法调用,而这些调用者希望得到的结果都各不相同。通常的操作就是再拼一个where语句上去:

var myResult = postDa.GetAllPost().Where(...)

但这时,很不幸的是,where语句中的条件并不是转换为native sql去执行的,它是在内存中筛选的。这是一个比较阴的性能问题。所以文章一开始我就建议大家多用SQL Profiler看看自己的LINQ是怎么执行的。

如果把返回类型换成IQueryable,那么你的where语句就可以转化为SQL执行。

public IQueryable<EdiBlog.Core.Entities.Post> GetAllPost()

{

return context.Post;

}

关于这两个接口,在StackOverflow上有一个比较好的帖子,大家可以自己看一下:

http://stackoverflow.com/questions/252785/what-is-the-difference-between-iqueryablet-and-ienumerablet

“IEnumerable: IEnumerable is best suitable for working with in-memory collection. IEnumerable doesn’t move between items, it is forward only collection.

IQueryable: IQueryable best suits for remote data source, like a database or web service. IQueryable is a very powerful feature that enables a variety of interesting deferred execution scenarios (like paging and composition based queries).”

在MSDN论坛上也有个比较直观的答案:

IQueryable returns a "queryable" that is a query you could still be enriched before really sending it to the server.

IEnumerable returns a list that is the actual querying took place and you get the results. ToList is isued to force running the query and returning these enumerable results...

So in short : - use IQueryable if you want to return a base query that could be further enhanced before running it server side (by enumerating its items).. - use IEnumerable/ToList if you want to return a list that has been retrieved from the db

计算个数,Count()和Count

这个是最容易被坑,也是非常严重的一个性能问题。当我们需要统计符合某条件的记录的条数时,我们希望SQL语句是SELECT COUNT(*) ... 这种形式的。然而下面这个看似很自然的写法却会导致不希望的结果:

context.Category.FirstOrDefault(p => p.Name == categoryName).Posts.Count;

这是我博客里用来统计某分类下文章数目的语句,当然,因为发现性能问题,现在已经不是这么写了。它产生的SQL并不是SELECT COUNT,而是分成2条。下面是SQL Profiler抓到的:

exec sp_executesql N'SELECT TOP (1) [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[DisplayName] AS [DisplayName]FROM [dbo].[Category] AS [Extent1]WHERE [Extent1].[Name] = @p__linq__0',N'@p__linq__0 nvarchar(4000)',@p__linq__0=N'ASPNET'exec sp_executesql N'SELECT [Extent2].[Id] AS [Id], [Extent2].[Title] AS [Title], [Extent2].[Slug] AS [Slug], [Extent2].[PubDate] AS [PubDate], [Extent2].[PostContent] AS [PostContent], [Extent2].[Author] AS [Author], [Extent2].[CommentEnabled] AS [CommentEnabled], [Extent2].[IsPublished] AS [IsPublished], [Extent2].[Hits] AS [Hits], [Extent2].[Rators] AS [Rators], [Extent2].[Rating] AS [Rating], [Extent2].[ExposedToSiteMap] AS [ExposedToSiteMap], [Extent2].[DisplayFrom] AS [DisplayFrom], [Extent2].[DisplayTill] AS [DisplayTill], [Extent2].[LastModifyOn] AS [LastModifyOn], [Extent2].[PublishToRss] AS [PublishToRss]FROM [dbo].[PostCategory] AS [Extent1]INNER JOIN [dbo].[Post] AS [Extent2] ON [Extent1].[PostId] = [Extent2].[Id]WHERE [Extent1].[CategoryId] = @EntityKeyValue1',N'@EntityKeyValue1 uniqueidentifier',@EntityKeyValue1='3FEB11A2-6E36-4DCE-8C02-614BEF7ACC62'

可以看到,EF做了两件事,第一件事是查找Name为"ASPNET"的Category,然后用这个Category的Id去找它所有的Post,最后做Count的其实是.NET在内存里进行的。这显然把我们不需要的信息都给SELECT出来了。我们只需要一个Count,为毛会这么复杂呢?

回顾第一条我所讲过的。不难发现。在FirstOrDefault(...)之后访问的属性,都是在内存里进行的。所以,当我们访问Category.FirstOrDefault(p => p.Name == categoryName)的时候,就生成了第一条SQL语句。紧跟其后的“.Posts”是Category对象的导航属性,EF会用lazy load去加载这个category所有的post,所以就生成了第二条SQL语句。再紧接其后的Count就自然而然在内存里进行了。

如果要让代码尽量去生成LINQ to SQL,有个很简单的原则,就是尽量用LINQ、Lambda表达式,这样EF才可能帮我们翻译。C#里的Count有两种。Enumerable.Count()是方法,List.Count是属性。一旦一个东西变成了List,你再去Count,就必定是在内存里进行的了。

所以,在EF中,要进行Count操作,应该这样写:

context.Post.Count(p => p.Categories.Any(q => q.Name == categoryName));

这时,Count()接受了一个lambda表达式,LINQ to SQL就能准确翻译为“SELECT COUNT”了:

SELECT [GroupBy1].[A1] AS [C1]

FROM (

SELECT COUNT(1) AS [A1]

FROM [dbo].[Post] AS [Extent1]

WHERE EXISTS (

SELECT 1 AS [C1]

FROM [dbo].[PostCategory] AS [Extent2]

INNER JOIN [dbo].[Category] AS [Extent3]

ON [Extent3].[Id] = [Extent2].[CategoryId]

WHERE ([Extent1].[Id] = [Extent2].[PostId])

AND ([Extent3].[Name] = 'ASPNET')

)

) AS [GroupBy1]

现在性能要明显好很多~

.NET编程委提醒您

ORM千万种,EF最方便,使用不规范,性能两行泪

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-02-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 汪宇杰博客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
最全数据结构详述: List VS IEnumerable VS IQueryable VS ICollection VS IDictionary
本文对常用的数据结构详述:Array, ArrayList,List,IList,ICollection, Stack, Queue, HashTable, Dictionary, IQueryabl
葡萄城控件
2018/01/10
2K0
最全数据结构详述: List VS IEnumerable VS IQueryable VS ICollection VS IDictionary
EFCore批量操作,你真的清楚吗
EntityFramework Core有许多新的特性,其中一个重要特性便是批量操作。批量操作意味着不需要为每次Insert/Update/Delete操作发送单独的命令,而是在一次SQL请求中发送批量组合指令。
心莱科技雪雁
2019/12/05
3.5K0
初探领域驱动设计(2)Repository在DDD中的应用
概述 上一篇我们算是粗略的介绍了一下DDD,我们提到了实体、值类型和领域服务,也稍微讲到了DDD中的分层结构。但这只能算是一个很简单的介绍,并且我们在上篇的末尾还留下了一些问题,其中大家讨论比较多的,也是我本人之前有一些疑问的地方就是Repository。我之前觉得IRepository和三层里面的IDAL很像,为什么要整出这么个东西来;有人说用EF的话就不需要Repository了;IRepository是鸡肋等等。 我觉得这些问题都很好,我自己也觉得有问题,带着这些问题我们就来看一看Repositor
用户1153966
2018/03/14
1.5K0
初探领域驱动设计(2)Repository在DDD中的应用
.NET EF Core(Entity Framework Core)
1、Entity Framework Core(EF Core)是微软官方的ORM框架。优点:功能强大、官方支持、生产效率高、力求屏蔽底层数据库差异;缺点:复杂、上手门槛高、不熟悉EFCore的话可能会进坑。 2、Dapper。优点:简单,N分钟即可上手,行为可预期性强;缺点:生产效率低,需要处理底层数据库差异。 3、EF Core是 模型驱动 (Model-Driven)的开发思想,Dapper是 数据库驱动(DataBase-Driven)的开发思想的。没有优劣,只有比较。 4、性能: Dapper等≠性能高;EF Core≠性能差。 5、EF Core是官方推荐、推进的框架,尽量屏蔽底层数据库差异,.NET开发者必须熟悉,根据的项目情况再决定用哪个。
鱼找水需要时间
2024/03/23
5450
.NET EF Core(Entity Framework Core)
Linq to Sql中Single写法不当可能引起的数据库查询性能低下
场景:需要从T_User表中返回指字条件的某条记录的某一个字段 在Linq中有二种理论上都行得通的写法,见下面的代码: Code using (dbUserDataContext db = new dbUserDataContext(Website.ConnStrdbUser))             { try {  //Guid _UserId = db.T_User.Single(c=>c.F_ID==new Guid("00000000-0000-0000-0000-000000000001"
菩提树下的杨过
2018/01/23
1.2K0
Entity Framework——并发策略
使用EF框架遇到并发时,一般采取乐观并发控制。 1支持并发检验 为支持并发检验,需要对实体进行额外的设置。默认情况下是不支持并发检验的。有以下两种方式: 方式名称 说明 时间戳注解/行版本 使用TimestampAttribute特性,实体的属性必须是byte数组类型 非时间戳注解 使用ConcurrencyCheckAttribute Fluent API 使用StringPropertyConfiguration.IsConcurrencyT
甜橙很酸
2018/04/17
1.1K0
Entity Framework 数据访问浅谈
在现代的软件开发中,数据库操作是必不可少的一部分。无论是简单的数据读取还是复杂的事务处理,都需要与数据库进行交互。在这个过程中,Entity Framework (EF) 作为 .NET 平台上的一款优秀 ORM(对象关系映射)框架,提供了强大的功能来简化数据库操作。本文将带你快速了解 EF 的基本用法,并探讨一些常见的问题以及如何避免这些错误。
Jimaks
2024/10/19
1530
Entity Framework 数据访问浅谈
.NET面试题系列[15] - LINQ:性能
当你使用LINQ to SQL时,请使用工具(比如LINQPad)查看系统生成的SQL语句,这会帮你发现问题可能发生在何处。
s055523
2018/09/14
2.6K0
.NET面试题系列[15] - LINQ:性能
Entity Framework快速入门--一对零到一关系处理
很久不更新blog了,正好趁着端午节的空,把之前一段时间使用关于EF以及工作上经验总结一下。
老马
2022/05/10
4000
Entity Framework快速入门--一对零到一关系处理
初级.NET程序员,你必须知道的EF知识和经验
注意:以下内容如果没有特别申明,默认使用的EF6.0版本,code first模式。 推荐MiniProfiler插件 工欲善其事,必先利其器。 我们使用EF和在很大程度提高了开发速度,不过随之带来的
逸鹏
2018/04/10
1.9K0
初级.NET程序员,你必须知道的EF知识和经验
浅析Entity Framework Core2.0的日志记录与动态查询条件
前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core2.0的日志记录与动态查询条件 去年我写过一篇关于Entity Framework Core1.0和1.1的日志记录和事务的文章: 一步步学习EF Core(2.事务与日志) 时过境迁..EF Core也更新到2.0了.. 在日志记录方面,有了比较大的变化..所以我觉得还是需要学习学习 正文 一、 Entity Framework Core2.
GuZhenYin
2018/03/30
1.5K0
浅析Entity Framework Core2.0的日志记录与动态查询条件
Entity Framework 简单查询
第一步还是先建立一个控制台的应用程序,然后通过Nuget添加Entity Framework。那么同时会给packages.config和App.config添加相应的配置。
aehyok
2018/09/11
8430
Entity Framework 简单查询
采用MiniProfiler监控EF与.NET MVC项目(Entity Framework 延伸系列1)
GuZhenYin
2018/01/04
1.2K0
采用MiniProfiler监控EF与.NET MVC项目(Entity Framework 延伸系列1)
.NET面试题系列[14] - LINQ to SQL与IQueryable
"理解IQueryable的最简单方式就是,把它看作一个查询,在执行的时候,将会生成结果序列。" - Jon Skeet
s055523
2018/09/14
1.7K0
.NET面试题系列[14] - LINQ to SQL与IQueryable
ASP.NET Core 入门教程 8、ASP.NET Core + Entity Framework Core 数据访问入门
本篇代码以下代码进行调整:https://github.com/ken-io/asp.net-core-tutorial/tree/master/chapter-02
KenTalk
2018/12/29
2.2K0
ASP.NET Core 入门教程 8、ASP.NET Core + Entity Framework Core 数据访问入门
linq to sql中慎用Where<T>(Func<TSource, bool> predicate),小心被Linq给"骗"了!
近日在一个大型Web项目中,采用Linq to Sql替换原来的sqlcommand/sqldatareader方式来获取数据,上线后刚开始一切正常,但是随着访问量的增加,网站明显慢了很多,监测服务器CPU占用率/内存使用情况等性能指标却发现均在正常范围内,无意中在SqlServer Profier中跟踪数据库执行的sql语句时,发现有大量语句直接将整个表的数据全部提取出来了,而非仅返回分页中的当前页数据! 而这些SQL都是Linq自动翻译并最终提交到数据库的,查看了相关的代码,明明写着Skip(n
菩提树下的杨过
2018/01/24
1K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-EF执行SQL语句与存储过程
这一节,我们来看看EF如何执行SQL语句与读取存储过程的数据,可能有一部分人,还不知道EF如何执行存储过程与原生SQL语句! 我们什么时候要直接使用原生的SQL语句? 返回值过于复杂 过于复杂的联合查询,可能连了好几张表 报表自定义SQL语句(自定义报表居多) 使用EF,但是写了一条性能很差的LINQ 批量操作 所以实际开发中,我往往两合一处理 EF上下文 DbContext包含了DataBase属性,里面有很多方法,但是实际我们只需要用到个方法      ExecuteSqlCommand 和 Sql
用户1149182
2018/03/27
1.2K0
ASP.NET MVC5+EF6+EasyUI 后台管理系统(89)-EF执行SQL语句与存储过程
编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>、LINQ避免迭代、LINQ替代迭代]
本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html 。本文主要学习记录以下内容:
aehyok
2018/08/31
9650
编写高质量代码改善C#程序的157个建议[IEnumerable<T>和IQueryable<T>、LINQ避免迭代、LINQ替代迭代]
Entity Framework 基础知识走马观花
  (1)通过选择以XML方式打开edmx文件,我们可以可以清楚地看到,edmx模型文件本质就是一个XML文件;
Edison Zhou
2018/08/20
1.4K0
Entity Framework 基础知识走马观花
Entity Framework快速入门--IQueryable与IEnumberable的区别
公开枚举器,该枚举器支持在指定类型的集合上进行简单迭代。也就是说:实现了此接口的object,就可以直接使用foreach遍历此object;
老马
2022/05/10
4540
Entity Framework快速入门--IQueryable与IEnumberable的区别
推荐阅读
相关推荐
最全数据结构详述: List VS IEnumerable VS IQueryable VS ICollection VS IDictionary
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文