在開始瞭解LINQ to Entities以前,須要先對.NET Framework 3.5版本後對C#語言的幾個擴展特性作一些闡釋,這有助於咱們更容易、更深入的理解LINQ to Entities技術的原理與實現。程序員
.NET Framework 3.5後,實例化局部變量語法有了新的選擇:使用「var」關鍵字隱式類型化:框架
var user = new User();
隱式類型化語法與Javascript語言裏聲明變量的語法相似,它能夠必定程度上簡化實例化局部變量的語法。但若是隱式實例化的做用僅限於此,那本文不會專門說起。隱式類型化更重要的用途是用於匿名類型的實例化。ui
須要提醒注意的是,只有局部變量實例化容許使用隱式實例化語法。這表示私有變量實例化沒法享受該福利;同時,僅聲明變量或實例化對象爲null也是不容許使用隱式實例化語法的。this
對象初始化指在實例化對象時,便可對對象的屬性進行賦值:編碼
1 var user = new User() 2 { 3 ID = Guid.NewGuid(), 4 Account = "Apollo" 5 };
LINQ to Entities的Select能夠將實體類型投影爲匿名類型,因此有必要對匿名類型做簡單介紹。匿名類型指的是不顯示聲明類型的細節,而是根據上下文環境須要,臨時聲明知足須要的類型。因爲該類型是臨時須要的,因此沒必要爲之命名。匿名類型的聲明語法以下:翻譯
var user = new { ID = Guid.NewGuid(), Name = "Apollo" };
擴展方法是微軟爲擴展已有框架而創造的一個語法糖,.NET Framework 3.5就是經過不少擴展方法實現了對.NET Framework 2.0的升級擴展的。擴展方法很神奇,被擴展的對象能夠不知道擴展方法的存在,就能在行爲上獲得擴展。擴展方法也很蹩腳,若是使用者不知情,極可能不知道對象具備擴展行爲;抑或知道有擴展方法,可是不知道要引用哪一個擴展庫以使其支持擴展的行爲。擴展方法的語法以下:code
1 public static class UserExt 2 { 3 public static void Drink(this User user, object water) 4 { 5 … 6 } 7 }
Lambda表達式是由委託以及匿名方法發展而來的,它可將表達式或代碼塊(匿名方法)賦給一個變量,從而以最少許的輸入實現編碼目的。Lambda表達式通常配合IEnumerable<T>的靜態擴展方法使用,完成對象集合的快捷查詢操做。Lambda表達式的語法以下:對象
var user = db.Users.FirstOrDefault(o => o.Account == "Apollo");
System.Linq.Enumerable靜態類聲明瞭一套標準查詢操做符(Standard Query Operators,SQO)方法集合。標準查詢操做符的語法和標準SQL很類似,這不是偶然,而是微軟有意爲之,以使熟悉SQL的程序員們更容易上手。標準查詢操做符的基本語法以下:blog
1 using (var db = new EntityContext()) 2 { 3 var roles = from o in db.Users 4 where o.Account == "Apollo" 5 select o.Roles; 6 … 7 }
標準查詢操做符和Lambda表達式的關係很是密切。編譯器會將上訴表達式轉化爲下列以Lambda表達式爲參數的顯式擴展方法調用序列:排序
1 using (var db = new EntityContext()) 2 { 3 var roles = db.Users.Where(o => o.Account == "Apollo").Select(o => o.Roles); 4 }
接下來,將針對數據查詢操做中經常使用的條件查詢、投影、分區、排序、分組、集合、元素、量詞和彙集等標準查詢操做符進行分類介紹。
條件操做符Where相似於SQL中的WHERE子句,用於實現條件查詢。下列擴展方法表達式查詢知足條件「角色不爲空」的用戶集合:
var user = db.Users.Where(o => o.Roles != null);
對應的標準查詢操做符表達式爲:
1 var users = from o in db.Users 2 where o.Roles != null 3 select o;
投影操做符Select相似於SQL中的SELECT子句,將對象投影爲一個匿名類型實例,用於控制指定查詢迭代器顯示或者處理的對象屬性。另外,須要注意的是,擴展方法表達式中的Select操做符並不是必須的,省略模式下,會返回完整的被投影對象。下列擴展方法表達式將用戶的賬號和密碼信息投影爲一個匿名類型:
var users = db.Users.Select(o => new { o.Account, o.Password });
對應的標準查詢操做符表達式爲:
1 var users = from o in db.Users 2 select new { o.Account, o.Password };
分區操做符實現對象的分區操做。其中,Take操做符相似於SQL中的TOP操做符,下列擴展方法表達式返回前5個用戶對象:
var users = db.Users.OrderBy(o => o.Roles.Count).Take(5);
Skip操做符用於跳過指定個數對象並返回序列中的剩餘對象,下列擴展方法表達式返回除前10個用戶外的剩餘用戶:
var users = db.Users.OrderBy(o => o.Roles.Count).Skip(10);
TakeWhile操做符用於返回條件表達式值爲真時的相鄰元素集合,下列擴展方法表達式返回第一個擁有3個角色的用戶以前的全部用戶集合:
var users = db.Users.OrderBy(o => o.Roles.Count).TakeWhile(o => o.Roles.Count == 3);
SkipWhile操做符用於跳過條件表達式值爲真時的元素,並返回剩下的元素集合,下列擴展方法表達式返回第一個擁有3個角色的用戶以後的全部用戶集合:
var users = db.Users.OrderBy(o => o.Roles.Count).SkipWhile(o => o.Roles == 3);
排序操做符實現對象的排序功能,包括OrderBy、OrderByDescending、ThenBy、ThenByDescending和Reverse五個操做符。其中OrderBy操做符實現對象的升序排列,至關於SQL中的ORDER BY ASC子句,下列擴展方法表達式實現用戶按擁有的角色數進行升序排列:
var users = db.Users.OrderBy(o => o.Roles.Count);
對應的標準查詢操做符表達式爲:
1 var users = from o in db.Users 2 orderby o.Roles.Count 3 select o;
OrderByDescending操做符實現對象的降序排列,至關於SQL中的ORDER BY DESC子句,下列擴展方法表達式實現用戶按擁有的角色數進行降序排列:
var users = db.Users.OrderByDescending(o => o.Roles.Count);
對應的標準查詢操做符表達式爲:
1 var users = from o in db.Users 2 orderby o.Roles.Count descending 3 select o;
ThenBy、ThenByDescending和Reverse操做符只能針對IOrderedEnumerable接口對象使用,因此通常緊跟在OrderBy/OrderByDesending操做符方法後使用。ThenBy操做符由編譯器翻譯爲對OrderBy操做符的再次調用;ThenByDescending操做符由編譯器翻譯爲對OrderByDescending操做符的再次調用;Reverse操做符實現對象的排序反向。這裏再也不一一舉例。
分組操做符GroupBy相似於SQL中的GROUP BY子句,實現對象的分組操做。下列擴展方法表達式實現用戶對象按擁有的角色數量進行分組:
var users = db.Users.GroupBy(o => o.Roles.Count);
對應的標準查詢操做符表達式爲:
1 var users = from o in db.Users 2 group o by o.Roles.Count into g 3 select new { RoleCount = g.Key, Group = g };
集合操做符包括Distinct、Union、intersect和Except四個操做符,除Distinct外,其餘三個操做符均可將兩個序列組合成一個序列。Distinct操做符相似於SQL中的DISTINCT關鍵字,用於刪除序列中具備重複值的對象。下列擴展方法表達式實現將用戶角色中的重複角色刪除功能:
var roles = user.Roles.Distinct();
Union操做符相似於SQL中的UNION關鍵字,用於求具備一樣結構的兩個序列的並集。下列擴展方法表達式實現將用戶1和用戶2所擁有的角色組合成一個角色集合,並排除其中重複的角色:
var roles = user1.Roles.Union(user2.Roles);
Intersect操做符相似於SQL中的INTERSECT關鍵字,用於求具備一樣結構的兩個序列的交集。下列擴展方法表達式返回用戶1和用戶2都具備的角色集合:
var roles = user1.Roles.Intersect(user2.Roles);
Except操做符相似於SQL中的EXCEPT關鍵字,用於返回第一個序列中有、但第二個序列中沒有的對象集合。下列擴展方法表達式返回用戶1擁有,而用戶2沒有的角色集合:
var roles = user1.Roles.Except(user2.Roles);
元素操做符包括兩組操做符,分別是用於從一個IEnumerable<T>序列中返回知足條件的單個對象或無知足條件對象時拋異常的First、Last和Single操做符,以及返回知足條件的單個對象或無知足條件對象時返回空對象的FirstOrDefault、LastOrDefault和SingleOrDefault操做符。其中First和FirstOrDefault操做符用於返回第一個知足條件的對象。下列擴展方法表達式返回第一個擁有三個角色的用戶:
var user = db.Users.FirstOrDefault(o => o.Roles.Count == 3);
Last和LastOrDefault操做符用於返回最後一個知足條件的對象。下列擴展方法表達式返回最後一個擁有三個角色的用戶:
var user = db.Users.LastOrDefault(o => o.Roles.Count == 3);
Single和SingleOrDefault操做符用於返回知足條件的序列中的惟一元素,若是序列中包含不止一個元素,將會拋異常。下列擴展方法表達式返回賬號爲「Apollo」的惟一用戶,若是有多個用戶賬號都爲「Apollo」,則拋異常:
var user = db.Users.SingleOrDefault(o => o.Account == "Apollo");
量詞操做符包括 Any、All和Contains三個操做符,用於檢查序列中是否有一些對象或全部對象知足條件。其中,Any操做符用於檢查序列中是否有任何一個對象知足條件。下列擴展方法表達式當有任何一個用戶擁有三個角色時返回真,不然返回假:
var result = db.Users.Any(o => o.Roles.Count == 3);
All操做符用於檢查序列中是否全部對象均知足條件。下列擴展方法表達式當全部用戶均擁有三個角色時返回真,不然返回假:
var result = db.Users.All(o => o.Roles.Count == 3);
Contains操做符用於檢查序列中是否包含指定的對象。下列擴展方法表達式當集合中包含用戶1則返回真,不然返回假:
var result = db.Users.Where(o => o.Roles.Count == 3).Contains(user1);
彙集操做符包括Count、Min、Max、Sum和Average等多個操做符,用於對對象集合進行統計計算。其中,Count操做符相似於SQL中的COUNT關鍵字,用於計算序列中知足條件的對象個數。下列擴展方法表達式返回擁有3個角色的用戶數量:
var result = db.Users.Count(o => o.Roles.Count == 3);
Min操做符相似於SQL中的MIN關鍵字,用於返回按條件計算的最小值。下列擴展方法表達式返回擁有最少角色數量的用戶所擁有的角色數量:
var result = db.Users.Min(o => o.Roles.Count);
Max操做符相似於SQL中的MAX關鍵字,用於返回按條件計算的最大值。下列擴展方法表達式返回擁有角色數量最多的用戶所擁有的角色數量:
var result = db.Users.Max(o => o.Roles.Count);
Sum操做符相似於SQL中的SUM關鍵字,用於返回按條件計算的總數。下列擴展方法表達式返回已賦予用戶的全部角色總數:
var result = db.Users.Sum(o => o.Roles.Count);
Average操做符相似於SQL中的AVERAGE關鍵字,用於返回按條件計算的平均值。下列擴展方法表達式,用於返回用戶所擁有的角色平均數:
var result = db.Users.Average(o => o.Roles.Count);
本文首先給出了LINQ技術的官方實現集合,以及LINQ to Entities實現的技術細節;而後概要介紹了與LINQ to Entities相關的幾個基本概念;最後對LINQ to Entities經常使用的標準查詢操做符的使用進行了分類介紹,從中也能夠看出LINQ to Entities和SQL的功能基本是一一對應的。