EntityFramework和EntityFramework.Extended使用說明——性能,語法和產生的sql

2021-12-10 dotNET跨平臺

環境說明:EntityFramework 6.1.3和.Net Framework4.5
性能注意事項:https://msdn.microsoft.com/zh-cn/library/cc853327.aspx
比較精髓的一點:查詢執行的各個階段中的準備查詢,每個唯一查詢一次。包括編寫查詢命令、基於模型和映射元數據生成命令樹和定義所返回數據的形狀的成本。 因為實體 SQL查詢命令和 LINQ 查詢現已緩存,所以,以後執行相同查詢所需的時間較少。 
如果有緩存的話,那麼查詢命令轉成sql語句的性能會進一步提高,是不是ORM的效率更接近Ado.Net了呢?

性能注意點:此處參考了 http://www.cnblogs.com/jake1/archive/2013/04/25/3043664.html
a.在資料庫裡面分頁
b.延遲加載要合理使用
c.需要連表的地方要連表查詢
d.查詢資料庫的次數和發出的sql語句的數量和長度
e.NoTracking的使用

表ContactInfo,GroupInfo說明:

CREATE TABLE [dbo].[ContactInfo](

    [ID] [int] IDENTITY(1,1) NOT NULL,

    [ContactId] [nvarchar](128) NOT NULL,

    [IsDelete] [int] NOT NULL,

    [Account] [nvarchar](64) NOT NULL,

    [ContactName] [nvarchar](50) NOT NULL,

    [CommonMobile] [nvarchar](50) NULL,

    [HeadPortrait] [nvarchar](256) NULL,

    [AttFile] [nvarchar](256) NULL,

    [GroupId] [int] NULL,

 CONSTRAINT [PK_ContactInfo] PRIMARY KEY CLUSTERED 

(

    [ID] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY]

CREATE TABLE [dbo].[GroupInfo](

    [GroupId] [int] IDENTITY(1,1) NOT NULL,

    [GroupName] [nvarchar](300) NOT NULL,

 CONSTRAINT [PK_GroupInfo] PRIMARY KEY CLUSTERED 

(

    [GroupId] ASC

)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

) ON [PRIMARY]

1.0 分頁查詢

c#語句:

var db = new PhoneBookEntities();db.GroupInfo.Where(c => c.GroupName.Length>=2).OrderByDescending(c => c.GroupId).Skip(2).Take(3).ToArray();

sql語句:

SELECT TOP (3) [Filter1].[GroupId] AS [GroupId], [Filter1].[GroupName] AS [GroupName]FROM ( SELECT [Extent1].[GroupId] AS [GroupId], [Extent1].[GroupName] AS [GroupName], row_number() OVER (ORDER BY [Extent1].[GroupId] DESC) AS [row_number]FROM [dbo].[GroupInfo] AS [Extent1]WHERE (LEN([Extent1].[GroupName])) >= 2) AS [Filter1]WHERE [Filter1].[row_number] > 2ORDER BY [Filter1].[GroupId] DESC

 

2.0 FirstOrDefault,First

c#語句:

var db = new PhoneBookEntities();db.GroupInfo.FirstOrDefault(c => c.GroupId == 1);

sql語句:

SELECT TOP (1) [Extent1].[GroupId] AS [GroupId], [Extent1].[GroupName] AS [GroupName]FROM [dbo].[GroupInfo] AS [Extent1]WHERE 1 = [Extent1].[GroupId]

 

延遲加載:

var db = new PhoneBookEntities();

var ci = db.ContactInfo.FirstOrDefault(c => c.ID == 9);

/* 此時產生的sql:*/

SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[ContactId] AS [ContactId], [Extent1].[IsDelete] AS [IsDelete], [Extent1].[Account] AS [Account], [Extent1].[ContactName] AS [ContactName], [Extent1].[CommonMobile] AS [CommonMobile], [Extent1].[HeadPortrait] AS [HeadPortrait], [Extent1].[AttFile] AS [AttFile], [Extent1].[GroupId] AS [GroupId]FROM [dbo].[ContactInfo] AS [Extent1]WHERE 9 = [Extent1].[ID]

var gn= ci.GroupInfo.GroupName;

/* (運行到此行c#代碼才會)產生sql:*/

exec sp_executesql N'SELECT [Extent1].[GroupId] AS [GroupId], [Extent1].[GroupName] AS [GroupName]FROM [dbo].[GroupInfo] AS [Extent1]WHERE [Extent1].[GroupId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

如果是一條數據,用延遲加載是OK的.如果上面查詢有多條如10條結果,每條結果都使用到GroupInfo屬性,那麼一共會有11條sql請求.效率低.應該使用連表,一條sql搞定.寫法如下.

第一種寫法 Join:

那如果是多條數據,應使用預加載.
c#語句:

var db = new PhoneBookEntities();

var ci = db.ContactInfo.Where(c => c.ID >3).Join(db.GroupInfo,c=>c.GroupId,g=>g.GroupId,(c,g)=>new{c.ContactName,g.GroupName});

foreach (var item in ci){    MessageBox.Show(item.ContactName + "->" + item.GroupName);}

sql語句:

SELECT [Extent1].[ID] AS [ID], [Extent1].[ContactName] AS [ContactName], [Extent2].[GroupName] AS [GroupName]FROM [dbo].[ContactInfo] AS [Extent1]INNER JOIN [dbo].[GroupInfo] AS [Extent2] ON [Extent1].[GroupId] = [Extent2].[GroupId]WHERE [Extent1].[ID] > 3

說明:GroupJoin的用法,和Join使用類似.區別在於第四個參數resultSelector.

Join的第四個參數是 Func<ContactInfo,GroupInfo,anonymous type>.
GroupJoin的第四個參數是 Func<ContactInfo,IEnumerable<GroupInfo>,anonymous type>.
使用場景:聯繫人和他的好友.聯繫人一張表,好友關係一張表.聯繫人表和好友關係表做連接,查出多個聯繫人數據(包含他的好友),就應該使用GroupJoin.
簡單說:
public partial class ContactInfo
{
public int ID { get; set; } 
public string ContactName { get; set; } 
public Nullable<int> GroupId { get; set; } 
public virtual GroupInfo GroupInfo { get; set; }
}
ContactInfo和GroupInfo一對一,該用Join;
如果是這種情況(僅僅是假設)
public partial class ContactInfo
{
public int ID { get; set; } 
public string ContactName { get; set; } 
public Nullable<int> GroupId { get; set; } 
public virtual List<GroupInfo> GroupInfo { get; set; }
}
ContactInfo和GroupInfo一對多,該用GroupJoin;

第二種寫法 Include:

注意:資料庫設計ContactInfo,GroupInfo 要有主外鍵關係.
c#語句:

var db = new PhoneBookEntities();var ci = db.ContactInfo.Include("GroupInfo").Where(c => c.ID > 3).Select(c => new { c.ContactName, c.GroupInfo.GroupName }); //或者 Include(c=>c.GroupInfo)foreach (var item in ci){    MessageBox.Show(item.ContactName + "->" + item.GroupName);}

sql語句:

SELECT [Extent1].[ID] AS [ID], [Extent1].[ContactName] AS [ContactName], [Extent2].[GroupName] AS [GroupName]FROM [dbo].[ContactInfo] AS [Extent1]LEFT OUTER JOIN [dbo].[GroupInfo] AS [Extent2] ON [Extent1].[GroupId] = [Extent2].[GroupId]WHERE [Extent1].[ID] > 3

3.0 Add

c#語句:

var db = new PhoneBookEntities();var giModel = new GroupInfo();giModel.GroupName = "Test";db.GroupInfo.Add(giModel);db.SaveChanges();//Add方法之後,會把資料庫表記錄中的GroupId給giModel.

sql語句:

exec sp_executesql N'INSERT [dbo].[GroupInfo]([GroupName])VALUES (@0)SELECT [GroupId]FROM [dbo].[GroupInfo]WHERE @@ROWCOUNT > 0 AND [GroupId] = scope_identity()',N'@0 nvarchar(300)',@0=N'Test'

 

4.0 AddRange

c#語句:

var db = new PhoneBookEntities();var gi = new GroupInfo[] { new GroupInfo() { GroupName = "g1" }, new GroupInfo() { GroupName = "g2" }, new GroupInfo() { GroupName = "g3" }, };db.GroupInfo.AddRange(gi);db.SaveChanges();

sql語句:

exec sp_executesql N'INSERT [dbo].[GroupInfo]([GroupName])VALUES (@0)SELECT [GroupId]FROM [dbo].[GroupInfo]WHERE @@ROWCOUNT > 0 AND [GroupId] = scope_identity()',N'@0 nvarchar(300)',@0=N'g1'exec sp_executesql N'INSERT [dbo].[GroupInfo]([GroupName])VALUES (@0)SELECT [GroupId]FROM [dbo].[GroupInfo]WHERE @@ROWCOUNT > 0 AND [GroupId] = scope_identity()',N'@0 nvarchar(300)',@0=N'g2'...

共執行3次.但是連接只打開關閉了1次.

5.0 Remove

c#語句:

var db = new PhoneBookEntities();var ci= db.GroupInfo.FirstOrDefault(c => c.GroupId == 214);db.GroupInfo.Remove(ci);db.SaveChanges();

sql語句:

SELECT TOP (1) [Extent1].[GroupId] AS [GroupId], [Extent1].[GroupName] AS [GroupName]FROM [dbo].[GroupInfo] AS [Extent1]WHERE 214 = [Extent1].[GroupId]exec sp_executesql N'DELETE [dbo].[GroupInfo]WHERE ([GroupId] = @0)',N'@0 int',@0=214

感覺繁瑣啦?解決方案,1,自己定義方法;2,引用EntityFramework.Extended

來源https://www.nuget.org/packages/EntityFramework.Extended/或者在當前項目裡,引用點擊右鍵,選擇管理NuGet程序包,聯機搜索並下載.
引用之後操作就簡單了.

c#語句:

var db = new PhoneBookEntities(); db.GroupInfo.Delete(c => c.GroupName == "Test");//過時的方法db.GroupInfo.Where(c => c.GroupName == "Test").Delete();//如果查到的記錄數為0,也不報錯.上邊的先查 FirstOrDefault 後刪 Remove,你猜猜報錯嗎db.SaveChanges();

 

sql語句:

DELETE [dbo].[GroupInfo]FROM [dbo].[GroupInfo] AS j0 INNER JOIN (SELECT [Extent1].[GroupId] AS [GroupId]FROM [dbo].[GroupInfo] AS [Extent1]WHERE N'Test' = [Extent1].[GroupName]) AS j1 ON (j0.[GroupId] = j1.[GroupId])

一條sql語句搞定刪除.

6.0 更新操作


基於EntityFramework.Extended的更新操作.
c#語句:

db.GroupInfo.Where(c => c.GroupName.Contains("g")).Update(c => new GroupInfo() { GroupName = c.GroupName+"!"});//此處沒有db.SaveChanges();,一樣執行了操作.

sql語句:

UPDATE [dbo].[GroupInfo] SET [GroupName] = [GroupName] + N'!' FROM [dbo].[GroupInfo] AS j0 INNER JOIN (SELECT [Extent1].[GroupId] AS [GroupId]FROM [dbo].[GroupInfo] AS [Extent1]WHERE [Extent1].[GroupName] LIKE N'%g%') AS j1 ON (j0.[GroupId] = j1.[GroupId])

也是一條sql語句搞定批量修改.

(注意:update語句中只set了GroupName欄位;
不用EntityFramework.Extended,用EF的先查後改,sql語句也是只set了GroupName欄位.
更特殊的情況,以下例子:

var gi = db.GroupInfo.FirstOrDefault(c => c.GroupId == 219 );//此條記錄的GroupName為"Test"gi.GroupName = "Test";db.SaveChanges();

EF會自動優化,最終結果只有一個select語句,而沒有update語句. 此處細節,贊!

對比NHibernate 4,以下兩行代碼產生的sql語句會set Product表的[所有]欄位
var pl = session.Query<Product>().FirstOrDefault(c => c.Name == "cnblogs");
pl.Name = "ICE";
)
如果先查出來要更改的數據,再修改.也是可以的. 但是從效率考慮,不管是c#寫法還是產生的sql語句,基於EntityFramework.Extended的更新操作更優.

7.0 EntityFramework.Extended中Future的使用

c#語句:

var db = new PhoneBookEntities();var fci = db.ContactInfo.Where(c => c.ID > 1).FutureFirstOrDefault();var fgi = db.GroupInfo.Where(c => c.GroupId > 2).FutureFirstOrDefault();ContactInfo ci= fci.Value;GroupInfo gi = fgi.Value;

//採用Future的寫法,不會立即查詢資料庫.只要調用結果的任意一個 .ToList,.ToArray或者.Value ,才會查資料庫.並且只發一個請求(Query #1 + Query #2 拼接好後發給資料庫,一起執行語句).

sql語句:

-- Query #1SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[ContactId] AS [ContactId], [Extent1].[IsDelete] AS [IsDelete], [Extent1].[Account] AS [Account], [Extent1].[ContactName] AS [ContactName], [Extent1].[CommonMobile] AS [CommonMobile], [Extent1].[HeadPortrait] AS [HeadPortrait], [Extent1].[AttFile] AS [AttFile], [Extent1].[GroupId] AS [GroupId]FROM [dbo].[ContactInfo] AS [Extent1]WHERE [Extent1].[ID] > 1;-- Query #2SELECT TOP (1) [Extent1].[GroupId] AS [GroupId], [Extent1].[GroupName] AS [GroupName]FROM [dbo].[GroupInfo] AS [Extent1]WHERE [Extent1].[GroupId] > 2;

標註:EntityFramework.Extended相關部分參考了 顧振印的博文: http://www.cnblogs.com/GuZhenYin/p/5482288.html

8.0 AsNoTracking

c#語句:

var db = new PhoneBookEntities();

var gi = db.GroupInfo.FirstOrDefault(c => c.GroupId == 219);MessageBox.Show(db.Entry(gi).State.ToString());//Unchanged

var giAnk = db.GroupInfo.AsNoTracking().FirstOrDefault(c => c.GroupId == 219);MessageBox.Show(db.Entry(giAnk).State.ToString());//Detached

相關文章:

原文地址:http://www.cnblogs.com/DKnight/p/5601680.html

.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注

相關焦點

  • MySQL sql_mode 說明(及處理一起 sql_mode 引發的問題)
    (命令行執行完語句後,show warnings 可看見)那麼解決這類問題的終極(推薦)辦法其實是,考慮到數據的兼容性和準確性,MySQL就應該運行在嚴格模式下!無論開發環境還是生產環境,否則代碼移植到線上可能產生隱藏的問題。sql_mode 問題可以很簡單,也可以很複雜。
  • 適用於初學者和分析師的SQL –使用Python入門SQL
    有很多不同的資料庫系統,但是最簡單和最容易使用的是SQLite。它快速,緊湊,並以易於共享的文件格式存儲數據。它用於每天人們使用的無數行動電話,計算機和各種其他應用程式中。最神奇的部分是它與Python捆綁在一起!哎呀,有一個原因使Facebook,Google,Dropbox等巨頭都使用SQLite!
  • Hive SQL基本使用詳解
    Hive 並非為聯機事務處理而設計,Hive 並不提供實時的查詢和基於行級的數據更新操作。Hive 的最佳使用場合是大數據集的批處理作業。Hive SQL 與 SQL的區別:HQL不支持行級別的增(insert into table values)、改、刪,所有數據在加載時就已經確定,不可更改。
  • SQL注入攻擊詳解
    本文將帶你從介紹 Web 應用運行原理開始,一步一步理解 Sql 注入的由來、原理和攻擊方式。,但是無論怎麼分常用的查詢數據類型總是以數字與字符來區分的,所以就會產生注入點為何種類型。id= x and 1=2 頁面運行錯誤,則說明此 Sql 注入為數字型注入。原因如下:當輸入 and 1=1時,後臺執行 Sql 語句:1select * from <表名> where id = x and 1=1沒有語法錯誤且邏輯判斷為正確,所以返回正常。
  • EntityFramework 7.0之初探
    註冊範圍(Registration scopes)在大部分情況下,特定提供者服務應該被註冊在DI中通過使用AddScoped方法,這是因為上下文創建Scope是為了在不同的上下文實例中使用不同的提供者,同時這也取決於在一個Scope 服務上的任何服務必須自己也作為Scope被註冊。
  • 使用explain和show profile來分析SQL語句實現優化SQL語句
    SQL語句優化是建立在慢查詢分析的基礎上,通過慢查詢定位有問題的SQL語句,關於慢查詢的介紹及其分析工具,可以參考[mysql慢查詢及慢查詢日誌分析工具]一、通過explain查詢1 用法:explain sql2 作用:用於分析sql語句
  • gorm原生SQL和SQL 構建器
    , 10000, 1), "jinzhu")注意 GORM 允許緩存預編譯 SQL 語句來提高性能,查看 性能 獲取詳情命名參數GORM 支持 sql.NamedArg、map[string]interface{}{} 或 struct 形式的命名參數,例如:
  • MySQL和SQL Server的區別
    因此,開發人員可以期待MySQL和SQL Server之間的一些相似之處,例如使用表來存儲數據,引用主鍵和外鍵,以及單個環境或伺服器中的多個資料庫。 將MySQL和SQL Server稱為現有的兩種最流行的RDBMS解決方案並不是不準確的,儘管Oracle和Postgres可能會對此有所說明。即使我們逐漸見證了從SQL向NoSQL的轉變,前者仍然是更具主導地位的存在。
  • spark結構化數據處理:Spark SQL、DataFrame和Dataset
    對於SQLContext,目前只有一個簡單的SQL語法解析器sql,而對於HiveContext,則可以使用hiveql和sql兩個解析器,默認是hiveql,我們可以通過如下語句來修改默認解析器:SQLContext.setConf("spark.sql.dialect", "sql")不過就目前來說,HiveQL解析器更加完善,因此推薦使用
  • BeetlSQL 3.0.0-M2 新增性能測試
    這次發布,主要是對性能進行第一輪測試,以驗證從2升級到3後,性能沒有顯著下降。
  • Spark SQL 之 Join 實現
    如下圖所示,sql語句被語法解析(SQL AST)成查詢計劃,或者我們通過Dataset/DataFrame提供的APIs組織成查詢計劃,查詢計劃分為兩大類:邏輯計劃和物理計劃,這個階段通常叫做邏輯計劃,經過語法分析(Analyzer)、一系列查詢優化(Optimizer)後得到優化後的邏輯計劃,最後被映射成物理計劃,轉換成RDD執行。
  • SQL語句的常用語法
    下面就為大家帶來一篇sql語句的常用語法 。個人覺得挺不錯的,學習MYSQL資料庫還是有點幫助的,給大家做個參考吧。
  • SQL語句性能調整之ORACLE的執行計劃
    如何產生執行計劃  要為一個語句生成執行計劃,可以有3種方法:  1).最簡單的辦法  Sql> set autotrace on  Sql> select * from dual;  執行完語句後,會顯示explain
  • SQL 中用 limit 為什麼會影響性能
    證實下面我們實際操作一下來證實上述的推論:為了證實select * from test where val=4 limit 300000,5是掃描300005個索引節點和300005個聚簇索引上的數據節點,我們需要知道MySQL有沒有辦法統計在一個sql中通過索引節點查詢數據節點的次數。我先試了Handler_read_*系列,很遺憾沒有一個變量能滿足條件。
  • SQL 性能起飛了!
    責編:架構君 | 來源:SimpleWu 連結:cnblogs.com/SimpleWu/p/9929043.html上一篇好文:萬字長文,理解Elasticsearch和面試總結這篇文章總結了 52 條 SQL 性能優化技巧,用簡潔明了的語言介紹給大家,不廢話全是乾貨,需要的可以收藏,退出。
  • EntityFramework Core上下文實例池原理
    當我們在Startup中注入上下文和上下文實例池時,其他參數配置我們暫且忽略,從使用上二者最大區別在於,上下文可自定義設置生命周期,默認為Scope,而上下文實例池可自定義最大池大小,默認為128。那麼問題來了,上下文實例池所管理的上下文的生命周期到底是什麼呢?
  • 面試必知的 Spark SQL 幾種 Join 實現
    如下圖所示,sql語句被語法解析(SQL AST)成查詢計劃,或者我們通過Dataset/DataFrame提供的APIs組織成查詢計劃,查詢計劃分為兩大類:邏輯計劃和物理計劃,這個階段通常叫做邏輯計劃,經過語法分析(Analyzer)、一系列查詢優化(Optimizer)後得到優化後的邏輯計劃,最後被映射成物理計劃,轉換成RDD執行。
  • 提升SQL語句性能的方法
    用具體案例進行SQL語句性能提升的方法。先用實際案例分析了優化SQL語句的方法,然後再結合nat123這個實際案例分析了如何實現外網訪問內網Mysql資料庫的方法。優化方法:優化的總體思路是拆分sql,將排序操作和查詢所有信息的操作分開。
  • 第三十三章 SQL命令 DROP INDEX
    Index-name是名稱的SQL版本,可以包括下劃線和其他標點符號。它作為SQL映射名稱列在表的Management Portal SQL Catalog Details中。ON table-name 或 ON TABLE table-name - 可選-與索引關聯的表的名稱。可以使用任一語法指定表名:第一個語法使用ON子句;TABLE關鍵字是可選的。
  • SQL 注入攻防入門詳解
    xp_cmdshell能執行dos命令,通過語句sp_dropextendedproc刪除,不過依然可以通過sp_addextendedproc來恢復,因此最好刪除或改名xplog70.dll(sql server 2000、windows7)xpsql70.dll(sqlserer 7.0)xp_fileexist用來確定一個文件是否存在xp_getfiledetails