日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Entity Framework 的一些性能建议

發布時間:2023/12/4 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Entity Framework 的一些性能建议 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點擊上方藍字關注“汪宇杰博客”

這是一篇我在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最方便,使用不規范,性能兩行淚


總結

以上是生活随笔為你收集整理的Entity Framework 的一些性能建议的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。