1. 程式人生 > 其它 >Linq to SQL -- 入門篇

Linq to SQL -- 入門篇

一、什麼是Linq

Linq是語言整合查詢(Language Integrated Query)的簡稱,是visual Studio 2008和.NET Framework 3.5版本中一項突破性的創新,它在物件領域和資料領域之間架起了一座橋樑。

Linq支援各種資料來源:

1、ADO.NET DataSet

2、XML文件

3、SQL Server資料庫

4、支援IEnumerable或泛型IEnumerable(T)介面的任意物件集合

5、更多。。。

二、Linq的優點

  • 傳統的SQL查詢
select FirstName,LastName,* from Customers
where city = 'Shanghai'
order by district

簡單的字串表示,沒有編譯時型別檢查,沒有IDE的智慧感知支援。

以上例子只是針對SQL,針對不同的資料來源,例如XML文件、各種WEB服務等我們還要學習不同的查詢方法。

  • Linq查詢示例

完全型別檢查和IDE智慧感知支援。

三、Linq查詢的步驟

所有的Linq查詢操作都由以下三個不同的操作組成:

  • 獲得資料來源
  • 建立查詢
  • 執行查詢

資料來源

  • 想要使用Linq進行查詢,資料來源必須支援IEnumerable或IEnumerable(T)泛型介面或派生介面(如泛型的IQueryable(T)介面)。

查詢

  • 查詢指定要從資料來源中檢索的資訊
  • 查詢還可以指定在返回這些資訊之前如何對其進行排序、分組和結構化
  • 查詢儲存在查詢變數中,並用查詢表示式進行初始化。為使編寫查詢的工作更加容易,C#引入了新的查詢語法

查詢執行

  • 查詢變數本身只是儲存查詢命令。實際的查詢執行會延遲在foreach語句中迴圈訪問變數時發生。此概念稱為“延遲執行”。
  • 強制立即執行,可以通過以下兩個方法,使得Linq立即執行查詢

   執行聚合函式(Count、Max、Average、First)。

   呼叫ToList(<T>Source)或ToArray(<T>Source)方法快取結果。

四、查詢基本操作

from子句

用於獲取資料來源

var queryAllCustomers=
from cust in Customers
select cust;
  • 查詢表示式必須以from子句開頭
  • 例子中cust是範圍變數,範圍變數類似於foreach迴圈中的迭代變數,但在查詢表示式中,實際上不發生迭代。執行查詢時,範圍變數將用作對Customers中的每個後續元素的引用。因為編譯器可以推斷cust的型別,所以不必顯示指定此型別。
  • Customers是資料來源,實現了IEnumerable或IEnumerable(T)或其派生介面的。

複合from子句

在某些情況下,原序列中的每個元素本身可能是序列,也可能包含序列。用於訪問某個資料來源中的內部集合。

需求:查詢出成績有90分以上的學生,得到他們的名字和成績

//資料來源
IList<Student> students = new List<Student>
{
    new Student{ Name="Kevin", Score=new List<int>{89,93,88,78}},
    new Student{ Name="Jackie",Score=new List<int>{92,87,83,91}},
    new Student{ Name="Helen",Score=new List<int>{53,76,72,62}}
};

//使用複合from子句查詢命令
var getStudent = 
        from student in students
        from score in student.Score
        where score > 90
        select new { Name=student.Name,Score=score};

分析:從程式碼中,我們可以看到學生物件中有個Score屬性,Score屬性本身就是List集合,這時候我們就要用到複合from子句進行查詢了。首先遍歷學生物件集合中的每個學生物件,然後在用另一個from子句,對每個學生物件中的Score屬性進行遍歷,篩選出含有90分以上的學生資訊進行返回。

使用let子句擴充套件範圍變數

用於建立查詢自身的範圍變數

需求:將字串陣列中的兩句英文語句中所有的母音字母打頭的單詞輸出到控制檯

string[] strings ={
                 "I am a new Student.",
                 "You are a talent"
                 };

var query = from sentences in strings
            let words = sentences.Split(' ')
            from word in words
            let w = word.ToLower()
            where w[0] == 'a' || w[0] == 'e' || w[0] == 'i' || w[0] == 'o' || 
                     w[0] == 'u'
            select word;

foreach (var word in query)
{
    Console.Write(word + ",");
}

分析:首先遍歷字串陣列中的每個字串,用let子句建立查詢自身的範圍變數words,並呼叫Split(' ')方法,將每個字串中以空格分割為單詞存入words變數中,然後再次使用let子句建立查詢自身的範圍變數word,並呼叫ToLower()方法,將每個單詞都變為小寫,最後篩選出首字母為母音的單詞進行返回。

說明:let語句是重新命名。let位於第一個from和select語句之間。

這個例子從聯接投影出最終“Let”表示式:

var q =
    from c in db.Customers
    join o in db.Orders on c.CustomerID
    equals o.CustomerID into ords
    let z = c.City + c.Country
    from o in ords
    select new
    {
        c.ContactName,
        o.OrderID,
        z
    };

where子句

將一個布林條件("謂詞")應用於每個源元素(由範圍變數引用),並返回滿足指定條件的元素。

適用場景:實現過濾,查詢等功能。

說明:與SQL命令中的Where作用相似,都是起到範圍限定也就是過濾作用的,而判斷條件就是它後面所接的子句。

Where操作包括3種形式,分別為簡單形式、關係條件形式、First()形式。下面分別用例項舉例下:

1)簡單形式:

例如:使用where篩選在倫敦的客戶

var q =
    from c in db.Customers
    where c.City == "London"
    select c;

再如:篩選1994 年或之後僱用的僱員:

var q =
    from e in db.Employees
    where e.HireDate >= new DateTime(1994, 1, 1)
    select e;

2)關係條件形式:

篩選庫存量在訂貨點水平之下但未斷貨的產品:

var q =
    from p in db.Products
    where p.UnitsInStock <= p.ReorderLevel && !p.Discontinued
    select p;

篩選出UnitPrice 大於10 或已停產的產品:

var q =
    from p in db.Products
    where p.UnitPrice > 10m || p.Discontinued
    select p;

下面這個例子是呼叫兩次where以篩選出UnitPrice大於10且已停產的產品。

var q =
    db.Products.Where(p=>p.UnitPrice > 10m).Where(p=>p.Discontinued);

3)First()形式:

返回集合中的一個元素,其實質就是在SQL語句中加TOP (1)。

簡單用法:選擇表中的第一個發貨方。

Shipper shipper = db.Shippers.First();

元素:選擇CustomerID 為“BONAP”的單個客戶

Customer cust = db.Customers.First(c => c.CustomerID == "BONAP");

條件:選擇運費大於 10.00 的訂單:

Order ord = db.Orders.First(o => o.Freight > 10.00M);

其他示例程式碼:

需求:將陣列中小於5的偶數查詢出來輸出到控制檯

//資料來源
int[] arr = { 0, 3, 2, 1, 9, 6, 8, 7, 4, 5 };

//使用Where子句查詢的查詢語句
var query = from a in arr
            where a < 5 && a % 2 == 0
            select a;

//執行查詢
foreach (var a in query)
{
    Console.WriteLine(a);
}

分析:首先遍歷陣列中的每個元素,然後用where語句篩選出小於5,並且對2去模是0的數查詢出來返回。

where子句不僅能使用表示式來進行篩選,還可以使用方法進行篩選。

public static bool IsEven(int a)
{
    return a % 2 == 0 ? true : false;
}
//資料來源
int[] arr = { 0, 3, 2, 1, 9, 6, 8, 7, 4, 5 };

//where子句也可以接受一個方法
var query = from a in arr
            where IsEven(a)
            select a;

foreach (var a in query)
{
    Console.Write(a + ",");
}

這就是一個在where語句中使用方法進行篩選的例子,輸出的結果和上例完全一樣。

使用where子句還要注意以下幾點

  • 一個查詢表示式可以包含多個where子句
  • where子句是一種篩選機制。除了不能是第一個或最後一個子句外,它幾乎可以放在查詢表示式中的任何位置。where子句可以出現在group子句的前面或後面,具體情況取決於是必須在對源元素進行分組之前還是分組之後來篩選源元素。
  • 如果指定的謂詞對於資料來源中的元素無效,則會發生編譯時錯誤。這是Linq提供的強型別檢查的一個優點。
  • 編譯時,where關鍵字會被轉換為對where標準查詢運算子方法的呼叫。

orderby子句

適用場景:對查詢出的語句進行排序,比如按時間排序等等。

說明:按指定表示式對集合排序;延遲,:按指定表示式對集合排序;延遲,預設是升序,加上descending表示降序,對應的擴充套件方法是OrderBy和OrderByDescending

  • 對查詢出來的結果集進行升序或降序排列。
  • 可以指定多個鍵,以便執行一個或多個次要排序操作。
  • 預設排序順序為升序。
  • 編譯時,orderby子句將被轉換為對OrderBy方法的呼叫。orderby子句中的多個鍵轉換為ThenBy方法呼叫。

接著上個例子的演示,本例是一個升序的排序。

var query = from a in arr
             where IsEven(a)
             orderby a ascending
             select a;

本例是一個降序的排序。

var query = from a in arr
 where IsEven(a)
 orderby a descending
 select a;

1)簡單形式

這個例子使用 orderby 按僱用日期對僱員進行排序:

var q =
    from e in db.Employees
    orderby e.HireDate
    select e;

說明:預設為升序

2)帶條件形式

注意:Where和Order By的順序並不重要。而在T-SQL中,Where和Order By有嚴格的位置限制。

var q =
    from o in db.Orders
    where o.ShipCity == "London"
    orderby o.Freight
    select o;

語句描述:使用where和orderby按運費進行排序。

3)降序排序

var q = 
    from p in db.Products
    orderby p.UnitPrice descending
    select p;

4)ThenBy

語句描述:使用複合的 orderby 對客戶進行排序,進行排序:

var q =
    from c in db.Customers
    orderby c.City, c.ContactName
    select c;

說明:按多個表示式進行排序,例如先按City排序,當City相同時,按ContactName排序。這一句用Lambda表示式像這樣寫:

var q = 
    db.Customers
    .OrderBy(c => c.City)
    .ThenBy(c => c.ContactName).ToList();

在T-SQL中沒有ThenBy語句,其依然翻譯為OrderBy,所以也可以用下面語句來表達:

var q = 
    db.Customers
    .OrderBy(c => c.ContactName)
    .OrderBy(c => c.City).ToList();

所要注意的是,多個OrderBy操作時,級連方式是按逆序。 對於降序的,用相應的降序操作符替換即可。

var q = 
    db.Customers
    .OrderByDescending(c => c.City)
    .ThenByDescending(c => c.ContactName).ToList();

需要說明的是,OrderBy操作,不支援按type排序,也不支援匿名類。比如

var q = 
    db.Customers
    .OrderBy(c => new
    {
        c.City,
        c.ContactName
    }).ToList();

會被丟擲異常。錯誤是前面的操作有匿名類,再跟OrderBy時,比較的是類別。比如

var q = 
    db.Customers
    .Select(c => new
    {
        c.City,
        c.Address
    })
    .OrderBy(c => c).ToList();

如果你想使用OrderBy(c => c),其前提條件是,前面步驟中,所產生的物件的類別必須為C#語言的基本型別。比如下句,這裡City為string型別。

var q = 
    db.Customers
    .Select(c => c.City)
    .OrderBy(c => c).ToList();

5)ThenByDescending

這兩個擴充套件方式都是用在OrderBy/OrderByDescending之後的,第一個ThenBy/ThenByDescending擴充套件方法作為第二位排序依據,第二個ThenBy/ThenByDescending則作為第三位排序依據,以此類推

var q =
    from o in db.Orders
    where o.EmployeeID == 1
    orderby o.ShipCountry, o.Freight descending
    select o;

語句描述:使用orderby先按發往國家再按運費從高到低的順序對 EmployeeID 1 的訂單進行排序。

6)帶GroupBy形式

var q = 
    from p in db.Products
    group p by p.CategoryID into g
    orderby g.Key
    select new {
        g.Key,
        MostExpensiveProducts =
            from p2 in g
            where p2.UnitPrice == g.Max(p3 => p3.UnitPrice)
            select p2
    };

語句描述:使用orderby、Max 和 Group By 得出每種類別中單價最高的產品,並按 CategoryID 對這組產品進行排序。

group子句

  • group子句返回一個IGrouping(T Key,T element)物件序列
  • 編譯時,group子句被轉換成對GroupBy方法的呼叫

需求:根據首字母分組,並列印到控制檯

//資料來源
string[] fruits = { "apple", "banana", "peach", "orange", "melon", "lemon" };

//分組查詢的查詢語句
var query = from f in fruits
            group f by f[0];

//執行查詢
foreach (var letters in query)
{
    Console.WriteLine("words that start with letter:" + letters.Key);
    foreach (var word in letters)
    {
        Console.WriteLine(word);
    }
}

分析:首先遍歷字串陣列中的每個字串,然後根據每個字串的首字母進行分組,返回結果

var query = from f in fruits
             group f by f[0] into g
             where g.Key == 'p' || g.Key == 'b'
             select g;

如果您想要對每個組執行附加查詢操作,則可以使用into上下文關鍵字指定一個臨時識別符號。使用into時,必須繼續編寫該查詢,並最終用一個select語句或另一個group子句結束該查詢。

多欄位分組示例

GroupBy(x => new { x.a , x.b, x.c }).Select( x=> ( new Class名 { a=x.Key.a , b=x.Key.b , c = x.Key.c } ))

join子句

  • 使用join子句可以將來自不同源序列並且在物件模型中沒有直接關係的元素相關聯。
  • 唯一的要求是每個源中的元素需要共享某個可以進行比較以判斷是否相等的值。
  • join子句使用特殊的equals關鍵字比較指定的鍵是否相等。

1)內部連線

var innerJoinQuery =
              from category in categories
              join prod in products on category.ID equals prod.CategoryID
                      select new { ProductName = prod.Name, Category = category.Name };

2)分組連線

 var innerGroupJoinQuery =
 from category in categories
 join prod in products on category.ID equals prod.CategoryID
 into prodGroup
 select new { CategoryName = category.Name, Products = prodGroup };

3)左外部連線

var leftOuterJoinQuery =
             from category in categories
             join prod in products on category.ID equals prod.CategoryID
             into prodGroup
                     from item in prodGroup.DefaultIfEmpty(new Product{Name =
             string.Empty, CategoryID = 0})
                     select new { CatName = category.Name, ProdName = item.Name };

在左外連線中,將返回左側源序列中的所有元素,即使它們在右側序列中沒有匹配的元素也是如此。

若要在Linq中執行左外連線,請將DefaultIfEmpty方法與分組連線結合起來,以指定要在某個元素不具有匹配元素時產生的預設右側元素,可以使用null作為任何引用型別的預設值。也可以指定使用者定義的預設型別。

equals關鍵字

  • join子句執行同等連線。換句話說,只能基於兩個鍵之間的相等關係進行匹配。
  • 為了表明所有連線都是同等連線,join子句使用equals關鍵字而不是==運算子

select子句(選擇、投影)

select子句可以指定將在執行查詢時產生的值的型別。該子句的結果將基於前面所有子句的計算結果以及select子句本身中的所有表示式。

查詢表示式必須以select子句或group子句結束

在最簡單的情況下,select子句僅指定範圍變數。這會使返回的序列包含於資料來源具有相同型別的元素。

轉https://www.cnblogs.com/zhaoyl9/p/10595104.html