【轉】LINQ to SQL語句(1)之Where

weixin_30639719發表於2020-04-05

 

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);

 

 

LINQ to SQL語句(2)之Select/Distinct

 

[1] Select介紹1

 

[2] Select介紹2

 

[3] Select介紹3和 Distinct介紹

 

Select/Distinct操作符

 

適用場景:o(∩_∩) o…查詢唄。

 

說明:和SQL命令中的select作用相似但位置不同,查詢表示式中的select及所接子句是放在表示式最後並把子句中的變數也就是結果返回回來;延遲。

 

Select/Distinct操作包括9種形式,分別為簡單用 法、匿名型別形式、條件形式、指定型別形式、篩選形式、整形型別形式、巢狀型別形式、本地方法呼叫形式、Distinct形式。

 

1.簡單用法:

 

這個示例返回僅含客戶聯絡人姓名的序列。

 

var q =

  from c in db.Customers

  select c.ContactName;

 

注意:這個語句只是一個宣告或者一個描述,並沒有真正把資料取出來,只有當你需要該資料的時候,它才會執行這個語句,這就是延遲載入(deferred loading)。如果,在宣告的時候就返回的結果集是物件的集合。你可以使用ToList() 或ToArray()方法把查詢結果先進行儲存,然後再對這個集合進行查詢。當然延遲載入(deferred loading)可以像拼接SQL語句那樣拼接查詢語法,再執行它。

 

2.匿名型別形式:

 

說明:匿名型別是C#3.0中新特性。其實質是編譯器根據我們自定義自動產生一個匿名的類來幫助我們實現臨時變數的儲存。匿名型別還依賴於另外一個特性:支援根據property來建立物件。比如,var d = new { Name = "s" };編譯器自動產生一個有property叫做Name的匿名類,然後按這 個型別分配記憶體,並初始化物件。但是var d = new {"s"};是編譯不 通過的。因為,編譯器不知道匿名類中的property的名字。例如stringc = "d";var d = new { c}; 則是可以通過編譯的。編譯器會建立一個叫 做匿名類帶有叫c的property。

 

例如下例:new {c,ContactName,c.Phone};ContactName和Phone都是在對映檔案中定義與表中字 段相對應的property。編譯器讀取資料並建立物件時,會建立一個匿名類,這個 類有兩個屬性,為ContactName和Phone,然後根據資料初始化物件。另外編譯器 還可以重新命名property的名字。

 

var q =

  from c in db.Customers

  select new {c.ContactName, c.Phone};

 

上面語句描述:使用 SELECT 和匿名型別返回僅含客戶聯絡人姓名和電話號碼的序列

 

var q =

  from e in db.Employees

  select new

  {

    Name = e.FirstName + " " + e.LastName,

    Phone = e.HomePhone

  };

 

上面語句描述:使用SELECT和匿名型別返回僅含僱員姓名和電話號碼的序列,並將 FirstName和LastName欄位合併為一個欄位“Name”,此外在所得的序列中將HomePhone欄位重新命名為Phone。

 

var q =

  from p in db.Products

  select new

  {

     p.ProductID,

    HalfPrice = p.UnitPrice / 2

  };

 

上面語句描述:使用SELECT和匿名型別返回所有產品的ID以及 HalfPrice(設定為產品單價除以2所得的值)的序列。

 

3.條件形式:

 

說明:生成SQL語句為:case when condition then else。

 

var q =

  from p in db.Products

  select new

  {

    p.ProductName,

    Availability =

     p.UnitsInStock - p.UnitsOnOrder < 0 ?

    "Out Of Stock" : "In Stock"

  };

 

上面語句描述:使用SELECT和條件語句返回產品名稱和產品供貨狀態的序列。

 

4.指定型別形式:

 

說明:該形式返回你自定義型別的物件集。

 

var q =

  from e in db.Employees

  select new Name

   {

    FirstName = e.FirstName,

    LastName = e.LastName

  };

 

上面語句描述:使用SELECT和已知型別返回僱員姓名的序列。

 

5.篩選形式:

 

說明:結合where使用,起到過濾作用。

 

var q =

  from c in db.Customers

   where c.City == "London"

  select c.ContactName;

 

上面語句描述:使用SELECT和WHERE返回僅含倫敦客戶聯絡人姓名的序列。

 

6.shaped形式(整形型別):

 

說明:其select操作使用了匿名物件,而這個匿名物件中,其屬性也是個匿名物件。

 

var q =

  from c in db.Customers

  select new {

    c.CustomerID,

    CompanyInfo = new {c.CompanyName, c.City, c.Country},

    ContactInfo = new {c.ContactName, c.ContactTitle}

  };

 

語句描述:使用 SELECT 和匿名型別返回有關客戶的資料的整形子集。查詢顧客的ID和公司資訊(公司名稱,城市,國家)以及聯絡資訊(聯絡人和職位)。

 

7.巢狀型別形式:

 

說明:返回的物件集中的每個物件DiscountedProducts屬性中,又包含一個集合。也就是每個物件也是一個集合類。

 

var q =

  from o in db.Orders

  select new {

     o.OrderID,

    DiscountedProducts =

      from od in o.OrderDetails

      where od.Discount > 0.0

      select od,

    FreeShippingDiscount = o.Freight

  };

 

語句描述:使用巢狀查詢返回所有訂單及其OrderID 的序列、打折訂單中專案的子序列以及免送貨所省下的金額。

 

8.本地方法呼叫形式(LocalMethodCall):

 

這個例子在查詢中呼叫本地方法 PhoneNumberConverter將電話號碼轉換為國際格式。

 

var q = from c in db.Customers

     where c.Country == "UK" || c.Country == "USA"

     select new

     {

       c.CustomerID,

       c.CompanyName,

       Phone = c.Phone,

       InternationalPhone =

       PhoneNumberConverter(c.Country, c.Phone)

     };

 

PhoneNumberConverter方法如下:

 

public string PhoneNumberConverter(stringCountry, string Phone)

{

  Phone = Phone.Replace(" ", "").Replace(")", ")-");

  switch (Country)

   {

    case "USA":

      return "1- " + Phone;

    case "UK":

       return "44-" + Phone;

    default:

      return Phone;

  }

}

 

下面也是使用了這個方法將電話號碼轉換為國際格式並建立XDocument

 

XDocument doc = new XDocument(

  new XElement("Customers", from c in db.Customers

       where c.Country == "UK" || c.Country == "USA"

       select (new XElement ("Customer",

           new XAttribute ("CustomerID", c.CustomerID),

           new XAttribute("CompanyName", c.CompanyName),

           new XAttribute("InterationalPhone",

            PhoneNumberConverter(c.Country, c.Phone))

           ))));

9.Distinct形式:

 

說明:篩選欄位中不相同的值。用於查詢不重複的結果集。生成SQL語句為:SELECT DISTINCT [City] FROM [Customers]

 

var q = (

  from c in db.Customers

  select c.City )

  .Distinct();

 

語句描述:查詢顧客覆蓋的國家。

 

 

LINQ to SQL語句(3)之Count/Sum/Min/Max/Avg

[1] Count/Sum講解

 

[2] Min講解

 

[3] Max講解

 

[4] Average和Aggregate講解

 

Count/Sum/Min/Max/Avg操作符

 

適用場景:統計資料吧,比如統計一些資料的個數,求和,最小值,最大值,平均數。

 

Count

 

說明:返回集合中的元素個數,返回INT型別;不延遲。生成 SQL語句為:SELECT COUNT(*) FROM

 

1.簡單形式:

 

得到資料庫中客戶的數量:

 

var q = db.Customers.Count();

 

2.帶條件形式:

 

得到資料庫中未斷貨產品的數量:

 

var q = db.Products.Count(p =>!p.Discontinued);

 

LongCount

 

說明:返回集合中的元素個數,返回LONG型別;不延遲。對於元素個數較多的集合可視情況可以選用LongCount來統計元素個數,它返回long型別,比較精確。生成 SQL語句為:SELECT COUNT_BIG(*) FROM

 

var q = db.Customers.LongCount();

 

Sum

 

說明:返回集合中數值型別元素之和,集合應為INT型別集合;不延遲。生成SQL語句為:SELECT SUM(…) FROM

 

1.簡單形式:

 

得到所有訂單的總運費:

 

var q = db.Orders.Select(o =>o.Freight).Sum();

 

2.對映形式:

 

得到所有產品的訂貨總數:

 

var q = db.Products.Sum(p =>p.UnitsOnOrder);

Min

說明:返回集合中元素的最小值;不延遲。生成SQL語句為:SELECT MIN(…) FROM

1.簡單形式:

查詢任意產品的最低單價:

var q = db.Products.Select(p => p.UnitPrice).Min();

 

2.對映形式:

 

查詢任意訂單的最低運費:

 

var q = db.Orders.Min(o => o.Freight);

 

 

3.元素:

 

查詢每個類別中單價最低的產品:

 

var categories =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    CategoryID = g.Key,

     CheapestProducts =

      from p2 in g

       where p2.UnitPrice == g.Min(p3 => p3.UnitPrice)

       select p2

  };

Max

說明:返回集合中元素的最大值;不延遲。生成SQL語句為:SELECT MAX(…) FROM

1.簡單形式:

查詢任意僱員的最近僱用日期:

var q = db.Employees.Select(e => e.HireDate).Max();

 

2.對映形式:

 

查詢任意產品的最大庫存量:

 

var q = db.Products.Max(p =>p.UnitsInStock);

 

3.元素:

 

查詢每個類別中單價最高的產品:

 

var categories =

  from p in db.Products

   group p by p.CategoryID into g

  select new {

     g.Key,

    MostExpensiveProducts =

      from p2 in g

      where p2.UnitPrice == g.Max(p3 => p3.UnitPrice)

      select p2

  };

Average

說明:返回集合中的數值型別元素的平均值。集合應為數字型別集合,其返回值型別為double;不延遲。生成SQL語句為:SELECT AVG(…) FROM

1.簡單形式:

得到所有訂單的平均運費:

var q = db.Orders.Select(o =>o.Freight).Average();

 

2.對映形式:

 

得到所有產品的平均單價:

 

var q = db.Products.Average(p => p.UnitPrice);

 

3.元素:

 

查詢每個類別中單價高於該類別平均單價的產品:

 

var categories =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    g.Key,

    ExpensiveProducts =

      from p2 in g

      where p2.UnitPrice > g.Average (p3 => p3.UnitPrice)

      select p2

  };

 

Aggregate

 

說明:根據輸入的表示式獲取聚合值;不延遲。即是說:用一個種子值與當前元素通過指定的函式來進行對比來遍歷集合中的元素,符合條件的元素保留下來。如果沒有指定種子值的話,種子值預設為集合的第一個元素。

 

 

LINQ to SQL語句(4)之Join

Join操作符

 

適用場景:在我們表關係中有一對一關係,一對多關係,多對多關係等。對各個表之間的關係,就用這些實現對多個表的操作。

 

說明:在Join操作中,分別為Join(Join查詢), SelectMany(Select一對多選擇) 和GroupJoin(分組Join查詢)。

 

該擴充套件方法對兩個序列中鍵匹配的元素進行inner join操作

 

SelectMany

 

說明:我們在寫查詢語句時,如果被翻譯成SelectMany需要滿足2個條件。1:查詢語句中沒有join和into,2:必須出現EntitySet。在我們表關係中有一對一關係,一對多關係,多對多關係等,下面分別介紹一下。

 

1.一對多關係(1 to Many):

 

var q =

  from c in db.Customers

  from o in c.Orders

  where c.City == "London"

  select o;

 

語句描述:Customers與Orders是一對多關係。即Orders在Customers類中以 EntitySet形式出現。所以第二個from是從c.Orders而不是db.Orders裡進行篩選。這個例子在From子句中使用外來鍵導航選擇倫敦客戶的所有訂單。

 

var q =

  from p in db.Products

  where p.Supplier.Country == "USA" &&p.UnitsInStock == 0

  select p;

 

語句描述:這一句使用了 p.Supplier.Country條件,間接關聯了Supplier表。這個例子在Where子句中使用外來鍵導航篩選其供應商在美國且缺貨的產品。生成SQL語句為:

 

SELECT [t0].[ProductID],[t0].[ProductName], [t0]. [SupplierID],

[t0].[CategoryID],[t0].[QuantityPerUnit],[t0].[UnitPrice],

[t0].[UnitsInStock],[t0].[UnitsOnOrder],[t0]. [ReorderLevel],

[t0].[Discontinued] FROM [dbo].[Products]AS [t0]

LEFT OUTER JOIN [dbo].[Suppliers] AS [t1]ON

[t1]. [SupplierID] = [t0].[SupplierID]

WHERE ([t1].[Country] = @p0) AND([t0].[UnitsInStock] = @p1)

-- @p0: Input NVarChar (Size = 3; Prec = 0;Scale = 0) [USA]

-- @p1: Input Int (Size = 0; Prec = 0;Scale = 0) [0]

 

2.多對多關係(Many to Many):

 

var q =

  from e in db.Employees

  from et in e.EmployeeTerritories

  where e.City == "Seattle"

  select new

  {

    e.FirstName,

     e.LastName,

    et.Territory.TerritoryDescription

  };

 

說明:多對多關係一般會涉及三個表(如果有一個表是自關聯的,那有可能只有2個表)。這一句語句涉及Employees, EmployeeTerritories, Territories三個表。它們的關係是1:M:1。Employees 和Territories沒有很明確的關係。

 

語句描述:這個例子在From子句中使用外來鍵導航篩選在西雅圖的僱員,同時列出其所在地區。這條生成SQL語句為:

 

SELECT [t0].[FirstName], [t0].[LastName],[t2]. [TerritoryDescription]

FROM [dbo].[Employees] AS [t0] CROSS JOIN[dbo].[EmployeeTerritories]

AS [t1] INNER JOIN [dbo]. [Territories] AS[t2] ON

[t2].[TerritoryID] = [t1].[TerritoryID]

WHERE ([t0].[City] = @p0) AND([t1].[EmployeeID] = [t0]. [EmployeeID])

-- @p0: Input NVarChar (Size = 7; Prec = 0;Scale = 0) [Seattle]

 

 

3.自聯接關係:

 

var q =

  from e1 in db.Employees

  from e2 in e1.Employees

  where e1.City == e2.City

  select new {

    FirstName1 = e1.FirstName, LastName1 = e1.LastName,

    FirstName2 = e2.FirstName, LastName2 = e2.LastName,

    e1.City

  };

 

語句描述:這個例子在select 子句中使用外來鍵導航篩選成對的僱員,每對中一個僱員隸屬於另一個僱員,且兩個僱員都來自相同城市。生成SQL語句為:

 

SELECT [t0].[FirstName] AS [FirstName1],[t0].[LastName] AS

[LastName1],[t1].[FirstName] AS[FirstName2], [t1].[LastName] AS

[LastName2],[t0].[City] FROM[dbo].[Employees] AS [t0],

[dbo].[Employees] AS [t1] WHERE([t0].[City] = [t1]. [City]) AND

([t1].[ReportsTo] = [t0].[EmployeeID])

GroupJoin

 

像上面所說的,沒有join和into,被翻譯成 SelectMany,同時有join和into時,那麼就被翻譯為GroupJoin。在這裡into的概念是對其結果進行重新命名。

 

1.雙向聯接(Two way join):

 

此示例顯式聯接兩個表並從這兩個表投影出結果:

 

var q =

   from c in db.Customers

  join o in db.Orders on c.CustomerID

  equals o.CustomerID into orders

  select new

   {

    c.ContactName,

    OrderCount = orders.Count ()

  };

 

說明:在一對多關係中,左邊是1,它每條記錄為c(from c in db.Customers),右邊是Many,其每條記錄叫做o ( join o in db.Orders ),每對應左邊的一個c,就會有一組o,那這一組o,就叫做orders, 也就是說,我們把一組o命名為orders,這就是into用途。這也就是為什麼在select語句中,orders可以呼叫聚合函式Count。在T-SQL中,使用其內嵌的T- SQL返回值作為欄位值。如圖所示:

 

 

 

生成SQL語句為:

 

SELECT [t0].[ContactName], (

  SELECT COUNT(*)

  FROM [dbo].[Orders] AS [t1]

   WHERE [t0].[CustomerID] = [t1].[CustomerID]

) AS [OrderCount]

FROM [dbo].[Customers] AS [t0]

 

2.三向聯接(There way join):

 

此示例顯式聯接三個表並分別從每個表投影出結果:

 

var q =

  from c in db.Customers

  join o in db.Orders on c.CustomerID

  equals o.CustomerID into ords

  join e in db.Employees on c.City

  equals e.City into emps

  select new

  {

    c.ContactName,

    ords = ords.Count(),

    emps = emps.Count()

  };

 

生成SQL語句為:

 

SELECT [t0]. [ContactName], (

  SELECT COUNT(*)

  FROM [dbo].[Orders] AS [t1]

  WHERE [t0].[CustomerID] = [t1].[CustomerID]

) AS [ords], (

SELECT COUNT(*)

  FROM [dbo].[Employees] AS [t2]

  WHERE [t0].[City] = [t2].[City]

) AS [emps]

FROM [dbo].[Customers] AS [t0]

 

3.左外部聯接(Left Outer Join):

 

此示例說明如何通過使用此示例說明如何通過使用 DefaultIfEmpty() 獲取左外部聯接。在僱員沒有訂單時,DefaultIfEmpty()方法返回null:

 

var q =

  from e in db.Employees

   join o in db.Orders on e equals o.Employee into ords

  from o in ords.DefaultIfEmpty()

  select new

  {

     e.FirstName,

    e.LastName,

    Order = o

   };

 

說明:以Employees左表,Orders右表,Orders 表中為空時,用null值填充。Join的結果重新命名ords,使用DefaultIfEmpty()函式對其再次查詢。其最後的結果中有個Order,因為from o in ords.DefaultIfEmpty() 是對 ords組再一次遍歷,所以,最後結果中的Order並不是一個集合。但是,如果沒有from o in ords.DefaultIfEmpty() 這句,最後的select語句寫成selectnew { e.FirstName, e.LastName, Order = ords }的話,那麼Order就是一個集合。

 

4.投影的Let賦值(Projectedlet assignment):

 

說明: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

  };

 

5.組合鍵(Composite Key):

 

這個例子顯示帶有組合鍵的聯接:

 

var q =

  from o in db.Orders

  from p in db.Products

  join d in db.OrderDetails

    on new

    {

      o.OrderID,

       p.ProductID

    } equals

      new

      {

        d.OrderID,

         d.ProductID

      }

    into details

  from d in details

  select new

  {

    o.OrderID,

    p.ProductID,

    d.UnitPrice

  };

 

說明:使用三個表,並且用匿名類來說明:使用三個表,並且用匿名類來表示它們之間的關係。它們之間的關係不能用一個鍵描述清楚,所以用匿名類,來表示組合鍵。還有一種是兩個表之間是用組合鍵表示關係的,不需要使用匿名類。

 

6.可為null/不可為null的鍵關係 (Nullable/Nonnullable Key Relationship):

 

這個例項顯示如何構造一側可為 null 而另一側不可為 null 的聯接:

 

var q =

   from o in db.Orders

  join e in db.Employees

    on o.EmployeeID equals

    (int?)e.EmployeeID into emps

  from e in emps

  select new

  {

     o.OrderID,

    e.FirstName

  };

 

LINQ to SQL語句(5)之Order By

Order By操作

 

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

 

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

 

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 =

  .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 對這組產品進行排序。

 

LINQ to SQL語句(6)之GroupBy/Having

Group By/Having操作符

 

適用場景:分組資料,為我們查詢資料縮小範圍。

 

說明:分配並返回對傳入引數進行分組操作後的可列舉物件。分組;延遲

 

1.簡單形式:

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select g;

 

語句描述:使用Group By按CategoryID劃分產品。

 

說明:from p in db.Products 表示從表中將產品物件取出來。group p by p.CategoryID into g表示對p按CategoryID欄位歸類。其結果命名為g,一旦重 新命名,p的作用域就結束了,所以,最後select時,只能select g。當然,也不必重新命名可以這樣寫:

 

var q =

  from p in db.Products

  group p by p.CategoryID;

 

我們用示意圖表示:

 

 

如果想遍歷某類別中所有記錄,這樣:

 

foreach (var gp in q)

{

  if (gp.Key == 2)

  {

     foreach (var item in gp)

    {

      //do something

    }

  }

}

 

2.Select匿名類:

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select new { CategoryID = g.Key, g };

 

說明:在這句LINQ語句中,有2個property:CategoryID和g。這個匿名類,其實質是對返回結果集重新進行了包裝。把g的property封裝成一個完整的分組。如下圖所示:

 

 

如果想遍歷某匿名類中所有記錄,要這麼做:

 

foreach (var gp in q)

{

  if (gp.CategoryID == 2)

  {

    foreach (var item in gp.g)

    {

      //do something

    }

  }

}

 

3.最大值

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    g.Key,

    MaxPrice = g.Max(p => p.UnitPrice)

  };

 

語句描述:使用Group By和Max查詢每個CategoryID的最高單價。

 

說明:先按CategoryID歸類,判斷各個分類產品中單價最大的 Products。取出CategoryID值,並把UnitPrice值賦給MaxPrice。

 

4.最小值

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    g.Key,

    MinPrice = g.Min(p => p.UnitPrice)

  };

 

語句描述:使用Group By和Min查詢每個CategoryID的最低單價。

 

說明:先按CategoryID歸類,判斷各個分類產品中單價最小的 Products。取出CategoryID值,並把UnitPrice值賦給MinPrice。

 

5.平均值

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    g.Key,

    AveragePrice = g.Average(p => p.UnitPrice)

  };

 

語句描述:使用Group By和Average得到每個CategoryID的平均單價。

 

說明:先按CategoryID歸類,取出CategoryID值和各個分類產品中單價的平均值。

 

6.求和

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    g.Key,

    TotalPrice = g.Sum(p => p.UnitPrice)

  };

 

語句描述:使用Group By和Sum得到每個CategoryID 的單價總計。

 

說明:先按CategoryID歸類,取出 CategoryID值和各個分類產品中單價的總和。

 

7.計數

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    g.Key,

    NumProducts = g.Count()

  };

 

語句描述:使用Group By和Count得到每個CategoryID中產品的數量。

 

說明:先按CategoryID歸類,取出 CategoryID值和各個分類產品的數量。

 

8.帶條件計數

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  select new {

    g.Key,

    NumProducts = g.Count(p => p.Discontinued)

  };

 

語句描述:使用Group By和Count得到每個CategoryID中斷貨產品的數量。

 

說明:先按 CategoryID歸類,取出CategoryID值和各個分類產品的斷貨數量。Count函式裡,使用了Lambda表示式,Lambda表示式中的p,代表這個組裡的一個元素或物件,即某一個產品。

 

9.Where限制

 

var q =

  from p in db.Products

  group p by p.CategoryID into g

  where g.Count() >= 10

  select new {

    g.Key,

    ProductCount = g.Count()

  };

 

語句描述:根據產品的―ID分組,查詢產品數量大於10的ID和產品數量。這個示例在Group By子句後使用Where子句查詢所有至少有10種產品的類別。

 

說明:在翻譯成SQL 語句時,在最外層巢狀了Where條件。

 

10.多列(Multiple Columns)

 

var categories =

  from p in db.Products

   group p by new

  {

    p.CategoryID,

     p.SupplierID

  }

    into g

    select new

      {

        g.Key,

        g

      };

 

語句描述:使用Group By按CategoryID和 SupplierID將產品分組。

 

說明:既按產品的分類,又按供應商分類。在 by後面,new出來一個匿名類。這裡,Key其實質是一個類的物件,Key包含兩個 Property:CategoryID、SupplierID。用g.Key.CategoryID可以遍歷CategoryID 的值。

 

11.表示式(Expression)

 

var categories =

   from p in db.Products

  group p by new { Criterion = p.UnitPrice > 10 } into g

  select g;

 

語句描述:使用Group By返回兩個產品序列。第一個序列包含單價大於10的產品。第二個序列包含單價小於或等於10的產品。

 

說明:按產品單價是否大於10分類。其結果分為兩類,大於的是一類,小於及等於為另一類。

 

LINQ to SQL語句(7)之Exists/In/Any/All/Contains

Exists/In/Any/All/Contains操作符

 

適用場景:用於判斷集合中元素,進一步縮小範圍。

 

Any

 

說明:用於判斷集合中是否有元素滿足某一條件;不延遲。(若條件為空,則集合只要不為空就返回True,否則為 False)。有2種形式,分別為簡單形式和帶條件形式。

 

1.簡單形式:

 

僅返回沒有訂單的客戶:

 

var q =

  from c in db.Customers

  where !c.Orders.Any()

  select c;

 

生成SQL語句為:

 

SELECT [t0].[CustomerID],[t0].[CompanyName], [t0].[ContactName],

[t0].[ContactTitle], [t0].[Address],[t0].[City], [t0].[Region],

[t0].[PostalCode], [t0].[Country],[t0].[Phone], [t0].[Fax]

FROM [dbo].[Customers] AS [t0]

WHERE NOT (EXISTS(

  SELECT NULL AS [EMPTY] FROM [dbo].[Orders] AS [t1]

  WHERE [t1].[CustomerID] = [t0]. [CustomerID]

  ))

 

2.帶條件形式:

 

僅返回至少有一種產品斷貨的類別:

 

var q =

  from c in db.Categories

  where c.Products.Any(p => p.Discontinued)

  select c;

 

生成SQL語句為:

 

SELECT [t0]. [CategoryID],[t0].[CategoryName], [t0].[Description],

[t0]. [Picture] FROM [dbo].[Categories] AS[t0]

WHERE EXISTS(

   SELECT NULL AS [EMPTY] FROM [dbo].[Products] AS [t1]

  WHERE ([t1].[Discontinued] = 1) AND

  ([t1].[CategoryID] = [t0]. [CategoryID])

  )

All

說明:用於判斷集合中所有元素是否都滿足某一條件;不延遲1.帶條件形式

 

var q =

  from c in db.Customers

  where c.Orders.All(o => o.ShipCity == c.City)

  select c;

 

語句描述:這個例子返回所有訂單都運往其所在城市的客戶或未下訂單的客戶。

 

Contains

 

說明:用於判斷集合中是否包含有某一元素;不延遲。它是對兩個序列進行連線操作的。

 

string[] customerID_Set =

  new string[] { "AROUT", "BOLID","FISSA" };

var q = (

  from o in db.Orders

  where customerID_Set.Contains(o.CustomerID)

  select o).ToList ();

 

語句描述:查詢"AROUT", "BOLID" 和 "FISSA" 這三個客戶的訂單。先定義了一個陣列,在LINQ to SQL中使用Contains,陣列中包含了所有的CustomerID,即返回結果中,所有的 CustomerID都在這個集合內。也就是in。你也可以把陣列的定義放在LINQ to SQL語句裡。比如:

 

var q = (

  from o in db.Orders

  where (

  new string[] { "AROUT", "BOLID","FISSA" })

  .Contains (o.CustomerID)

  select o).ToList();

Not Contains則取反:

var q = (

  from o in db.Orders

  where !(

  new string[] { "AROUT", "BOLID","FISSA" })

  .Contains(o.CustomerID)

  select o).ToList();

 

1.包含一個物件:

 

var order = (from o in db.Orders

       where o.OrderID == 10248

       select o).First();

var q = db.Customers.Where(p =>p.Orders.Contains(order)).ToList();

foreach (var cust in q)

{

  foreach (var ord in cust.Orders)

  {

    //do something

  }

}

 

語句描述:這個例子使用Contain查詢哪個客戶包含OrderID為10248的訂單。

 

2.包含多個值:

 

string[] cities =

  new string[] { "Seattle", "London","Vancouver", "Paris" };

var q = db.Customers.Where(p=>cities.Contains(p.City)).ToList();

 

語句描述:這個例子使用Contains查詢其所在城市為西雅圖、倫敦、巴黎或溫哥華的客戶。

 

 

LINQ to SQL語句(8)之Concat/Union/Intersect/Except

Concat/Union/Intersect/Except操作

 

適用場景:對兩個集合的處理,例如追加、合併、取相同項、相交項等等。

 

Concat(連線)

 

說明:連線不同的集合,不會自動過濾相同項;延遲。

 

1.簡單形式:

var q = (

     from c in db.Customers

     select c.Phone

    ).Concat(

     from c in db.Customers

     select c.Fax

    ).Concat(

     from e in db.Employees

     select e.HomePhone

    );

 

語句描述:返回所有消費者和僱員的電話和傳真。

 

2.複合形式:

var q = (

     from c in db.Customers

     select new

     {

       Name = c.CompanyName,

       c.Phone

     }

     ).Concat(

     from e in db.Employees

     select new

     {

       Name = e.FirstName + " " + e.LastName,

       Phone = e.HomePhone

     }

    );

 

語句描述:返回所有消費者和僱員的姓名和電話。

 

Union(合併)

 

說明:連線不同的集合,自動過濾相同項;延遲。即是將兩個集合進行合併操作,過濾相同的項。

 

var q = (

     from c in db.Customers

     select c.Country

    ).Union(

     from e in db.Employees

     select e.Country

    );

 

語句描述:查詢顧客和職員所在的國家。

 

Intersect(相交)

 

說明:取相交項;延遲。即是獲取不同集合的相同項(交集)。即先遍歷第一個集合,找出所有唯一的元素,然後遍歷第二個集合,並將每個元素與前面找出的元素作對比,返回所有在兩個集合內都出現的元素。

 

var q = (

     from c in db.Customers

     select c.Country

    ).Intersect (

     from e in db.Employees

     select e.Country

    );

 

語句描述:查詢顧客和職員同在的國家。

 

Except(與非)

 

說明:排除相交項;延遲。即是從某集合中刪除與另一個集合中相同的項。先遍歷第一個集合,找出所有唯一的元素,然後再遍歷第二個集合,返回第二個集合中所有未出現在前面所得元素集合中的元素。

 

var q = (

     from c in db.Customers

     select c.Country

    ).Except(

     from e in db.Employees

     select e.Country

    );

 

語句描述:查詢顧客和職員不同的國家。

 

LINQ to SQL語句(9)之Top/Bottom和Paging和SqlMethods

Top/Bottom操作

 

適用場景:適量的取出自己想要的資料,不是全部取出,這樣效能有所加強。

 

Take

 

說明:獲取集合的前n個元素;延遲。即只返回限定數量的結果集。

 

var q = (

  from e in db.Employees

  orderby e.HireDate

  select e)

  .Take(5);

 

語句描述:選擇所僱用的前5個僱員。

 

Skip

 

說明:跳過集合的前n個元素;延遲。即我們跳過給定的數目返回後面的結果集。

 

var q = (

  from p in db.Products

  orderby p.UnitPrice descending

  select p)

  .Skip (10);

 

語句描述:選擇10種最貴產品之外的所有產品。

 

TakeWhile

 

說明:直到某一條件成立就停止獲取;延遲。即用其條件去依次判斷源序列中的元素,返回符合判斷條件的元素,該判斷操作將在返回 false或源序列的末尾結束。

 

SkipWhile

 

說明:直到某一條件成立就停止跳過;延遲。即用其條件去判斷源序列中的元素並且跳過第一個符合判斷條件的元素,一旦判斷返回false,接下來將不再進行判斷並返回剩下的所有元素。

 

Paging(分頁)操作

 

適用場景:結合Skip和Take就可實現對資料分頁操作。

 

1.索引

var q = (

  from c in db.Customers

  orderby c.ContactName

  select c)

  .Skip(50)

  .Take(10);

 

語句描述:使用Skip和Take運算子進行分頁,跳過前50條記錄,然後返回接下來10條記錄,因此提供顯示 Products表第6頁的資料。

 

2.按唯一鍵排序

var q = (

   from p in db.Products

  where p.ProductID > 50

   orderby p.ProductID

  select p)

  .Take(10);

 

語句描述:使用Where子句和Take運算子進行分頁,首先篩選得到僅50 (第5頁最後一個ProductID)以上的ProductID,然後按ProductID排序,最後取前10個結果,因此提供Products表第6頁的資料。請注意,此方法僅適用於按唯一鍵排序的情況。

 

SqlMethods操作

 

在LINQ to SQL語句中,為我們提供了 SqlMethods操作,進一步為我們提供了方便,例如Like方法用於自定義通配表示式,Equals用於相比較是否相等。

 

Like

 

自定義的通配表示式。%表示零長度或任意長度的字串;_表示一個字元;[]表示在某範圍區間的一個字元;[^]表示不在某範圍區間的一個字元。比如查詢消費者ID以“C”開頭的消費者。

 

var q = from c in db.Customers

     where SqlMethods.Like(c.CustomerID, "C%")

     select c;

 

比如查詢消費者ID沒有“AXOXT”形式的消費者:

 

var q = from c in db.Customers

    where ! SqlMethods.Like(c.CustomerID, "A_O_T")

    select c;

DateDiffDay

 

說明:在兩個變數之間比較。分別有:DateDiffDay、 DateDiffHour、DateDiffMillisecond、DateDiffMinute、DateDiffMonth、 DateDiffSecond、DateDiffYear 

 

var q = from o in db.Orders

    where SqlMethods

    .DateDiffDay (o.OrderDate, o.ShippedDate) < 10

    select o;

 

語句描述:查詢在建立訂單後的 10 天內已發貨的所有訂單。

 

已編譯查詢操作(Compiled Query)

 

說明:在之前我們沒有好的方法對寫出的SQL語句進行編輯重新查詢,現在我們可以這樣做,看下面一個例子:

 

//1. 建立compiled query

NorthwindDataContext db = newNorthwindDataContext();

var fn = CompiledQuery.Compile(

   (NorthwindDataContext db2, string city) =>

  from c in db2.Customers

  where c.City == city

  select c);

//2.查詢城市為London的消費者,用LonCusts集合表示,這時可以用資料控制元件 繫結

var LonCusts = fn(db, "London");

//3.查詢城市 為Seattle的消費者

var SeaCusts = fn(db, "Seattle");

 

語句描述:這個例子建立一個已編譯查詢,然後使用它檢索輸入城市的客戶。

 

LINQ to SQL語句(10)之Insert

插入(Insert)1.簡單形式

 

說明:new一個物件,使用InsertOnSubmit方法將其加入到對應的集合中,使用SubmitChanges()提交到資料庫。

 

NorthwindDataContext db = newNorthwindDataContext();

var newCustomer = new Customer

{

  CustomerID = "MCSFT",

  CompanyName = "Microsoft",

  ContactName = "John Doe",

  ContactTitle = "Sales Manager",

  Address = "1 Microsoft Way",

  City = "Redmond",

  Region = "WA",

  PostalCode = "98052",

   Country = "USA",

  Phone = "(425) 555- 1234",

  Fax = null

};

db.Customers.InsertOnSubmit(newCustomer);

db.SubmitChanges ();

 

語句描述:使用InsertOnSubmit方法將新客戶新增到Customers 表物件。呼叫SubmitChanges 將此新Customer儲存到資料庫。

 

2.一對多關係

 

說明:Category與Product是一對多的關係,提交Category(一端)的資料時,LINQ to SQL會自動將Product(多端)的資料一起提交。

 

var newCategory = new Category

{

  CategoryName = "Widgets",

  Description = "Widgets are the ……"

};

var newProduct = new Product

{

  ProductName = "Blue Widget",

  UnitPrice = 34.56M,

  Category = newCategory

};

db.Categories.InsertOnSubmit(newCategory);

db.SubmitChanges ();

 

語句描述:使用InsertOnSubmit方法將新類別新增到Categories 表中,並將新Product物件新增到與此新Category有外來鍵關係的Products表中。呼叫SubmitChanges將這些新物件及其關係儲存到資料庫。

 

3.多對多關係

 

說明:在多對多關係中,我們需要依次提交。

 

var newEmployee = new Employee

{

  FirstName = "Kira",

  LastName = "Smith"

};

var newTerritory = new Territory

{

  TerritoryID = "12345",

  TerritoryDescription = "Anytown",

  Region = db.Regions.First()

};

var newEmployeeTerritory = newEmployeeTerritory

{

   Employee = newEmployee,

  Territory = newTerritory

};

db.Employees.InsertOnSubmit(newEmployee);

db.Territories.InsertOnSubmit(newTerritory);

db.EmployeeTerritories.InsertOnSubmit(newEmployeeTerritory);

db.SubmitChanges();

 

語句描述:使用InsertOnSubmit方法將新僱員新增到Employees 表中,將新Territory新增到Territories表中,並將新 EmployeeTerritory物件新增到與此新Employee物件和新Territory物件有外來鍵關係的EmployeeTerritories表中。呼叫SubmitChanges將這些新物件及其關係保持到資料庫。

 

4.使用動態CUD重寫(Overrideusing Dynamic CUD)

 

說明:CUD就是Create、Update、Delete的縮寫。下面的例子就是新建一個ID(主鍵) 為32的Region,不考慮資料庫中有沒有ID為32的資料,如果有則替換原來的資料,沒有則插入。

 

Region nwRegion = new Region()

{

  RegionID = 32,

  RegionDescription = "Rainy"

};

db.Regions.InsertOnSubmit(nwRegion);

db.SubmitChanges ();

 

語句描述:使用DataContext提供的分部方法InsertRegion插入一個區域。對SubmitChanges 的呼叫呼叫InsertRegion 重寫,後者使用動態CUD執行Linq To SQL生成的預設SQL查詢。

 

LINQ to SQL語句(11)之Update

更新(Update)

 

說明:更新操作,先獲取物件,進行修改操作之後,直接呼叫SubmitChanges()方法即可提交。注意,這裡是在同一個DataContext中,對於不同的DataContex看下面的講解。

 

1.簡單形式

Customer cust =

  db.Customers.First(c => c.CustomerID == "ALFKI");

cust.ContactTitle = "VicePresident";

db.SubmitChanges();

 

語句描述:使用 SubmitChanges將對檢索到的一個Customer物件做出的更新保持回資料庫。

 

2.多項更改

var q = from p in db.Products

     where p.CategoryID == 1

    select p;

foreach (var p in q)

{

  p.UnitPrice += 1.00M;

}

db.SubmitChanges ();

 

語句描述:使用SubmitChanges將對檢索到的進行的更新保持回資料庫。

 

LINQ to SQL語句(12)之Delete和使用Attach

刪除(Delete)1.簡單形式

 

說明:呼叫DeleteOnSubmit方法即可。

 

OrderDetail orderDetail =

   db.OrderDetails.First

  (c => c.OrderID == 10255 && c.ProductID == 36);

db.OrderDetails.DeleteOnSubmit(orderDetail);

db.SubmitChanges();

 

語句描述:使用 DeleteOnSubmit方法從OrderDetail 表中刪除OrderDetail物件。呼叫 SubmitChanges 將此刪除保持到資料庫。

 

2.一對多關係

 

說明:Order 與OrderDetail是一對多關係,首先DeleteOnSubmit其OrderDetail(多端),其次 DeleteOnSubmit其Order(一端)。因為一端是主鍵。

 

var orderDetails =

  from o in db.OrderDetails

  where o.Order.CustomerID == "WARTH" &&

   o.Order.EmployeeID == 3

  select o;

var order =

   (from o in db.Orders

   where o.CustomerID == "WARTH" && o.EmployeeID ==3

   select o).First();

foreach (OrderDetail od in orderDetails)

{

   db.OrderDetails.DeleteOnSubmit(od);

}

db.Orders.DeleteOnSubmit(order);

db.SubmitChanges();

 

語句描述語句描述:使用DeleteOnSubmit方法從Order 和Order Details表中刪除Order和Order Detail物件。首先從Order Details刪除,然後從Orders刪除。呼叫SubmitChanges將此刪除保持到資料庫。

 

3.推理刪除(Inferred Delete)

 

說明:Order與OrderDetail是一對多關係,在上面的例子,我們全部刪除CustomerID為WARTH和EmployeeID為3 的資料,那麼我們不須全部刪除呢?例如Order的OrderID為10248的OrderDetail有很多,但是我們只要刪除 ProductID為11的OrderDetail。這時就用Remove方法。

 

Order order = db.Orders.First(x =>x.OrderID == 10248);

OrderDetail od =

  order.OrderDetails.First(d => d.ProductID == 11);

order.OrderDetails.Remove(od);

db.SubmitChanges();

 

語句描述語句描述:這個例子說明在實體物件的引用實體將該物件從其EntitySet 中移除時,推理刪除如何導致在該物件上發生實際的刪除操作。僅當實體的關聯對映將DeleteOnNull設定為true且CanBeNull 為false 時,才會發生推理刪除行為。

 

使用Attach更新(Updatewith Attach)

 

說明:在對於在不同的 DataContext之間,使用Attach方法來更新資料。例如在一個名為tempdb的 NorthwindDataContext中,查詢出Customer和Order,在另一個 NorthwindDataContext中,Customer的地址更新為123 First Ave,Order的 CustomerID 更新為CHOPS。

 

//通常,通過從其他層反序列化 XML 來獲取要附加的實體

//不支援將實體從一個DataContext附加到另一個DataContext

//因此若要複製反序列化實體的操作,將在此處重新建立這 些實體

Customer c1;

List<Order> deserializedOrders = newList<Order>();

Customer deserializedC1;

using (NorthwindDataContext tempdb = newNorthwindDataContext())

{

  c1 = tempdb.Customers.Single(c => c.CustomerID =="ALFKI");

  deserializedC1 = new Customer

   {

    Address = c1.Address,

    City = c1.City,

    CompanyName = c1.CompanyName,

    ContactName = c1.ContactName,

    ContactTitle = c1.ContactTitle,

    Country = c1.Country,

    CustomerID = c1.CustomerID,

    Fax = c1.Fax,

    Phone = c1.Phone,

     PostalCode = c1.PostalCode,

    Region = c1.Region

  };

  Customer tempcust =

     tempdb.Customers.Single(c => c.CustomerID == "ANTON");

  foreach (Order o in tempcust.Orders)

  {

     deserializedOrders.Add(new Order

    {

       CustomerID = o.CustomerID,

      EmployeeID = o.EmployeeID,

      Freight = o.Freight,

       OrderDate = o.OrderDate,

      OrderID = o.OrderID,

      RequiredDate = o.RequiredDate,

      ShipAddress = o.ShipAddress,

      ShipCity = o.ShipCity,

      ShipName = o.ShipName,

      ShipCountry = o.ShipCountry,

      ShippedDate = o.ShippedDate,

      ShipPostalCode = o.ShipPostalCode,

      ShipRegion = o.ShipRegion,

      ShipVia = o.ShipVia

    });

  }

}

using (NorthwindDataContext db2 = newNorthwindDataContext())

{

  //將第一個實體附加到當前資料上下文,以跟蹤更改

  //對Customer更新,不能寫錯

   db2.Customers.Attach(deserializedC1);

  //更改所跟蹤的實體

  deserializedC1.Address = "123 First Ave";

  // 附加訂單列表中的所有實體

  db2.Orders.AttachAll (deserializedOrders);

  //將訂單更新為屬於其他客戶

   foreach (Order o in deserializedOrders)

  {

     o.CustomerID = "CHOPS";

  }

  //在當前資料上下文中提交更改

  db2.SubmitChanges();

}

 

語句描述:從另一個層中獲取實體,使用Attach和AttachAll將反序列化後的實體附加到資料上下文,然後更新實體。更改被提交到資料庫。

 

使用Attach更新和刪除(Update and Delete with Attach)

 

說明:在不同的DataContext中,實現插入、更新、刪除。看下面的一個例子:

 

//通常,通過從其他層 反序列化XML獲取要附加的實體

//此示例使用 LoadWith 在一個查詢中預 先載入客戶和訂單,

//並禁用延遲載入

Customer cust = null;

using (NorthwindDataContext tempdb = newNorthwindDataContext())

{

  DataLoadOptions shape = new DataLoadOptions();

   shape.LoadWith<Customer>(c => c.Orders);

  //載入第一個客戶實體及其訂單

  tempdb.LoadOptions = shape;

   tempdb.DeferredLoadingEnabled = false;

  cust = tempdb.Customers.First(x => x.CustomerID =="ALFKI");

}

Order orderA = cust.Orders.First();

Order orderB = cust.Orders.First(x =>x.OrderID > orderA.OrderID);

using (NorthwindDataContext db2 = newNorthwindDataContext())

{

  //將第一個實體附加到當前資料上下文,以跟蹤更改

   db2.Customers.Attach(cust);

  //附加相關訂單以進行跟蹤; 否則將在提交時插入它們

  db2.Orders.AttachAll(cust.Orders.ToList ());

  //更新客戶的Phone.

  cust.Phone = "2345 5436";

  //更新第一個訂單OrderA的ShipCity.

   orderA.ShipCity = "Redmond";

  //移除第二個訂單 OrderB.

  cust.Orders.Remove(orderB);

  //新增一個新的訂單Order到客戶Customer中.

  Order orderC = new Order() { ShipCity = "New York" };

  cust.Orders.Add (orderC);

  //提交執行

  db2.SubmitChanges();

}

 

語句描述:從一個上下文提取實體,並使用 Attach 和 AttachAll 附加來自其他上下文的實體,然後更新這兩個實體,刪除一個實體,新增另一個實體。更改被提交到資料庫。

 

LINQ to SQL語句(13)之開放式併發控制和事務

Simultaneous Changes開放式併發控制

 

下表介紹 LINQ to SQL 文件中涉及開放式併發的術語:

 

術語說明

併發兩個或更多使用者同時嘗試更新同一資料庫行的情形。

併發衝突兩個或更多使用者同時嘗試向一行的一列或多列提交衝突值的情形。

併發控制用於解決併發衝突的技術。

開放式併發控制先調查其他事務是否已更改了行中的值,再允許提交更改的技術。相比之下,保守式併發控制則是通過鎖定記錄來避免發生併發衝突。之所以稱作開放式控制,是因為它將一個事務干擾另一事務視為不太可能發生。

衝突解決通過重新查詢資料庫重新整理出現衝突的項,然後協調差異的過程。重新整理物件時,LINQ to SQL 更改跟蹤器會保留以下資料:最初從資料庫獲取並用於更新檢查的值通過後續查詢獲得的新資料庫值。 LINQ to SQL 隨後會確定相應物件是否發生衝突(即它的一個或多個成員值是否已發生更改)。如果此物件發生衝突,LINQ to SQL 下一步會確定它的哪些成員發生衝突。LINQ to SQL 發現的任何成員衝突都會新增到衝突列表中。

 

 

在 LINQ to SQL 物件模型中,當以下兩個條件都得到滿足時,就會發生“開放式併發衝突”:客戶端嘗試向資料庫提交更改;資料庫中的一個或多個更新檢查值自客戶端上次讀取它們以來已得到更新。此衝突的解決過程包括查明物件的哪些成員發生衝突,然後決定您希望如何進行處理。

 

開放式併發(Optimistic Concurrency)

 

說明:這個例子中在你讀取資料之前,另外一個使用者已經修改並提交更新了這個資料,所以不會出現衝突。

 

//我們開啟一個新的連線來模擬另外一個使用者

NorthwindDataContext otherUser_db = newNorthwindDataContext();

var otherUser_product =

  otherUser_db.Products.First(p => p.ProductID == 1);

otherUser_product.UnitPrice = 999.99M;

otherUser_db.SubmitChanges();

//我們當前連線

var product = db.Products.First(p =>p.ProductID == 1);

product.UnitPrice = 777.77M;

try

{

  db.SubmitChanges();//當前連線執行成功

}

catch (ChangeConflictException)

{

}

 

說明:我們讀取資料之後,另外一個使用者獲取並提交更新了這個資料,這時,我們更新這個資料時,引起了一個併發衝突。系統發生回滾,允許你可以從資料庫檢索新更新的資料,並決定如何繼續進行您自己的更新。

 

//當前 使用者

var product = db.Products.First(p =>p.ProductID == 1);

//我們開啟一個新的連線來模擬另外一個使用者

NorthwindDataContext otherUser_db = newNorthwindDataContext() ;

var otherUser_product =

  otherUser_db.Products.First(p => p.ProductID == 1);

otherUser_product.UnitPrice = 999.99M;

otherUser_db.SubmitChanges();

//當前使用者修改

product.UnitPrice = 777.77M;

try

{

   db.SubmitChanges();

}

catch (ChangeConflictException)

{

  //發生異常!

}

 

Transactions事務

 

LINQto SQL 支援三種事務模型,分別是:

 

顯式本地事務:呼叫 SubmitChanges 時,如果 Transaction 屬性設定為事務,則在同一事務的上下文中執行 SubmitChanges 呼叫。成功執行事務後,要由您來提交或回滾事務。與事務對應的連線必須與用於構造 DataContext 的連線匹配。如果使用其他連線,則會引發異常。

 

顯式可分發事務:可以在當前 Transaction 的作用域中呼叫 LINQ to SQL API(包括但不限於 SubmitChanges)。LINQ to SQL 檢測到呼叫是在事務的作用域內,因而不會建立新的事務。在這種情況下, <token>vbtecdlinq</token> 還會避免關閉連線。您可以在此類事 務的上下文中執行查詢和SubmitChanges 操作。

 

隱式事務:當您呼叫 SubmitChanges 時,LINQ to SQL 會檢查此呼叫是否在 Transaction 的作用域內或者 Transaction 屬性是否設定為由使用者啟動的本地事務。如果這兩個事務它均未找到,則 LINQ to SQL 啟動本地事務,並使用此事務執行所生成的 SQL 命令。當所有 SQL 命令均已成功執行完畢時,LINQ to SQL 提交本地事務並返回。

 

1.Implicit(隱式)

 

說明:這個例子在執行SubmitChanges ()操作時,隱式地使用了事務。因為在更新2種產品的庫存數量時,第二個產品庫存數量為負數了,違反了伺服器上的 CHECK 約束。這導致了更新產品全部失敗了,系統回滾到這個操作的初始狀態。

try

{

  Product prod1 = db.Products.First(p => p.ProductID == 4);

  Product prod2 = db.Products.First(p => p.ProductID == 5);

   prod1.UnitsInStock -= 3;

  prod2.UnitsInStock -= 5;//錯誤:庫存 數量的單位不能是負數

  //要麼全部成功要麼全部失敗

   db.SubmitChanges();

}

catch (System.Data.SqlClient.SqlExceptione)

{

  //執行異常處理

}

 

2.Explicit(顯式)

 

說明:這個例子使用顯式事務。通過在事務中加入對資料的讀取以防止出現開放式併發異常,顯式事務可以提供更多的保護。如同上一個查詢中,更新 prod2 的 UnitsInStock 欄位將使該欄位為負值,而這違反了資料庫中的 CHECK 約束。這導致更新這兩個產品的事務失敗,此時將回滾所有更改。

 

using (TransactionScope ts = new TransactionScope())

{

  try

  {

     Product prod1 = db.Products.First(p => p.ProductID == 4);

    Product prod2 = db.Products.First(p => p.ProductID == 5);

    prod1.UnitsInStock -= 3;

    prod2.UnitsInStock -= 5;//錯誤:庫存數量的單位不能是負數

    db.SubmitChanges();

  }

  catch (System.Data.SqlClient.SqlException e)

  {

    //執行異常處理

  }

}

 

LINQ to SQL語句(14)之Null語義和DateTime

Null語義

 

說明:下面第一個例子說明查詢ReportsToEmployee為null的僱員。第二個例子使用Nullable<T>.HasValue查詢僱員,其結果與第一個例 子相同。在第三個例子中,使用Nullable<T>.Value來返回ReportsToEmployee不為null的僱員的ReportsTo的值。

 

1.Null

 

查詢不隸屬於另一個僱員的所有僱員:

 

var q =

  from e in db.Employees

  where e.ReportsToEmployee == null

   select e;

2.Nullable<T>.HasValue

 

查詢不隸屬於另一個僱員的所有僱員:

 

var q =

  from e in db.Employees

  where !e.ReportsTo.HasValue

  select e;

3.Nullable<T>.Value

 

返回前者的EmployeeID 編號。請注意 .Value 為可選:

 

var q =

  from e in db.Employees

  where e.ReportsTo.HasValue

  select new

  {

    e.FirstName,

    e.LastName,

    ReportsTo = e.ReportsTo.Value

  };

日期函式

 

LINQ to SQL支援以下 DateTime方法。但是,SQLServer和CLR的DateTime型別在範圍和計時週期精度上不同,如下表。

 

型別最小值 最大 值 計時週期

System.DateTime 0001 年 1 月 1 日 9999 年 12 月 31 日 100 毫微秒(0.0000001秒)

T-SQL DateTime 1753 年 1 月 1 日 9999 年 12 月 31 日 3.33… 毫秒(0.0033333 秒)

T-SQL SmallDateTime 1900 年 1 月 1 日 2079 年 6 月 6 日 1 分鐘(60 秒)

 

 

CLR DateTime 型別與SQL Server型別相比,前者範圍更 大、精度更高。因此來自SQLServer的資料用CLR型別表示時,絕不會損失量值或精度。但如果反過來的話,則範圍可能會減小,精度可能會降低;SQL Server 日期不存在TimeZone概念,而在CLR中支援這個功能。

 

我們在LINQ to SQL查詢使用以當地時間、UTC 或固定時間要自己執行轉換。

 

下面用三個例項說明一下。

 

1.DateTime.Year

var q =

  from o in db.Orders

  where o.OrderDate.Value.Year == 1997

   select o;

 

語句描述:這個例子使用DateTime 的Year 屬性查詢1997 年下的訂單。

 

2.DateTime.Month

var q =

  from o in db.Orders

  where o.OrderDate.Value.Month == 12

  select o;

 

語句描述:這個例子使用DateTime的Month屬性查詢十二月下的訂單。

 

3.DateTime.Day

var q =

  from o in db.Orders

  where o.OrderDate.Value.Day == 31

  select o;

 

語句描述:這個例子使用DateTime的Day屬性查詢某月 31 日下的訂單。

 

 

LINQ to SQL語句(15)之String

字串(String)

 

LINQ to SQL支援以下String方法。但是不同的是預設 情況下System.String方法區分大小寫。而SQL則不區分大小寫。

 

1.字串串聯(StringConcatenation)

var q =

  from c in db.Customers

  select new

  {

     c.CustomerID,

    Location = c.City + ", " + c.Country

  };

 

語句描述:這個例子使用+運算子在形成經計算得出的客戶Location值過程中將字串欄位和字串串聯在一起。

 

2.String.Length

var q =

  from p in db.Products

  where p.ProductName.Length < 10

  select p;

 

語句描述:這個例子使用Length屬性查詢名稱短於10個字元的所有產品。

 

3.String.Contains(substring)

var q =

  from c in db.Customers

  where c.ContactName.Contains ("Anders")

  select c;

 

語句描述:這個例子使用Contains方法查詢所有其聯絡人姓名中包含“Anders”的客戶。

 

4.String.IndexOf(substring)

var q =

  from c in db.Customers

  select new

  {

     c.ContactName,

    SpacePos = c.ContactName.IndexOf(" ")

  };

 

語句描述:這個例子使用IndexOf方法查詢每個客戶聯絡人姓名中出現第一個空格的位置。

 

5.String.StartsWith (prefix)

var q =

  from c in db.Customers

  where c.ContactName.StartsWith("Maria")

  select c;

 

語句描述:這個例子使用StartsWith方法查詢聯絡人姓名以“Maria”開頭的客戶。

 

6.String.EndsWith(suffix)

var q =

  from c in db.Customers

  where c.ContactName.EndsWith("Anders")

  select c;

 

語句描述:這個例子使用EndsWith方法查詢聯絡人姓名以“Anders”結尾的客戶。

 

7.String.Substring(start)

var q =

  from p in db.Products

  select p.ProductName.Substring(3);

 

語句描述:這個例子使用Substring方法返回產品名稱中從第四個字母開始的部分。

 

8.String.Substring (start, length)

var q =

  from e in db.Employees

   where e.HomePhone.Substring(6, 3) == "555"

  select e;

 

語句描述:這個例子使用Substring方法查詢家庭電話號碼第七位到第九位是“555”的僱員。

 

9.String.ToUpper()

var q =

  from e in db.Employees

  select new

  {

    LastName = e.LastName.ToUpper(),

    e.FirstName

  };

 

語句描述:這個例子使用ToUpper方法返回姓氏已轉換為大寫的僱員姓名。

 

10.String.ToLower()

var q =

  from c in db.Categories

  select c.CategoryName.ToLower();

 

語句描述:這個例子使用ToLower方法返回已轉換為小寫的類別名稱。

 

11.String.Trim()

var q =

  from e in db.Employees

  select e.HomePhone.Substring(0, 5).Trim ();

 

語句描述:這個例子使用Trim方法返回僱員家庭電話號碼的前五位,並移除前導和尾隨空格。

 

12.String.Insert(pos, str)

var q =

  from e in db.Employees

  where e.HomePhone.Substring(4, 1) == ")"

  select e.HomePhone.Insert(5, ":");

 

語句描述:這個例子使用 Insert方法返回第五位為 ) 的僱員電話號碼的序列,並在 ) 後面插入一個 :。

 

13.String.Remove(start)

var q =

  from e in db.Employees

  where e.HomePhone.Substring(4, 1) == ") "

  select e.HomePhone.Remove(9);

 

語句描述:這個例子使用Remove方法返回第五位為 ) 的僱員電話號碼的序列,並移除從第十個字元開始的所有字元。

 

14.String.Remove(start, length)

var q =

  from e in db.Employees

  where e.HomePhone.Substring(4, 1) == ")"

  select e.HomePhone.Remove(0, 6);

 

語句描述:這個例子使用Remove方法返回第五位為 ) 的僱員電話號碼的序列,並移除前六個字元。

 

15.String.Replace(find, replace)

var q =

  from s in db.Suppliers

  select new

  {

     s.CompanyName,

    Country = s.Country

    .Replace ("UK", "United Kingdom")

    .Replace ("USA", "United States of America")

  };

 

語句描述:這個例子使用 Replace 方法返回 Country 欄位中UK 被替換為 United Kingdom 以及USA 被替換為 United States of America 的供 應商資訊。

 

 

LINQ to SQL語句(16)之物件標識

物件標識

 

執行庫中的物件具有唯一標識。引用同一物件的兩個變數實際上是引用此物件的同一例項。你更改一個變數後,可以通過另一個變數看到這些更改。

 

關聯式資料庫表中的行不具有唯一標識。由於每一行都具有唯一的主鍵,因此任何兩行都不會共用同一鍵值。

 

實際上,通常我們是將資料從資料庫中提取出來放入另一層中,應用程式在該層對資料進行處理。這就是 LINQ to SQL 支援的模型。將資料作為行從資料庫中提取出來時,你不期望表示相同資料的兩行實際上對應於相同的行例項。如果您查詢特定客戶兩次,您將獲得兩行資料。每一行包含相同的資訊。

 

對於物件。你期望在你反覆向 DataContext 索取相同的資訊時,它實際上會為你提供同一物件例項。你將它們設計為層次結構或關係圖。你希望像檢索實物一樣檢索它們,而不希望僅僅因為你多次索要同一內容而收到大量的複製例項。

 

在 LINQ to SQL 中, DataContext 管理物件標識。只要你從資料庫中檢索新行,該行就會由其主鍵記錄到標識表中,並且會建立一個新的物件。只要您檢索該行,就會將原始物件例項傳遞回應用程式。通過這種方式,DataContext 將資料庫看到的標識(即主鍵)的概念轉換成相應語言看到的標識(即例項)的概念。應用程式只看到處於第一次檢索時的狀態的物件。新資料如果不同,則會被丟棄。

 

LINQ to SQL 使用此方法來管理本地物件的完整性,以支援開放式更新。由於在最初建立物件 後唯一發生的更改是由應用程式做出的,因此應用程式的意向是很明確的。如果在中間階段外部某一方做了更改,則在呼叫 SubmitChanges() 時會識別出這些更改。

 

以上來自MSDN,的確,看了有點“正規”,下面我用兩個例子說明一下。

 

物件快取

 

在第一個示例中,如果我們執行同一查詢兩次,則每次都會收到對記憶體中同一物件的引用。很明顯,cust1和cust2是同一個物件引用。

 

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

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

 

下面的示例中,如果您執行返回資料庫中同一行的不同查詢,則您每次都會收到對記憶體中同一物件的引用。cust1和cust2是同一個物件引用,但是資料庫查詢了兩次。

 

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

Customer cust2 = (

  from o in db.Orders

  where o.Customer.CustomerID == "BONAP"

  select o )

  .First()

  .Customer;

 

LINQ to SQL語句(17)之物件載入

物件載入延遲載入

 

在查詢某物件時,實際上你只查詢該物件。不會同時自動獲取這個物件。這就是延遲載入。

 

例如,您可能需要檢視客戶資料和訂單資料。你最初不一定需要檢索與每個客戶有關的所有訂單資料。其優點是你可以使用延遲載入將額外資訊的檢索操作延遲到你確實需要檢索它們時再進行。請看下面的示例:檢索出來CustomerID,就根據這個ID查詢出OrderID。

 

var custs =

   from c in db.Customers

   where c.City == "Sao Paulo"

   select c;

//上面 的查詢句法不會導致語句立即執行,僅僅是一個描述性的語句,

只有需要的時候才會執行它

foreach (var cust in custs)

{

   foreach (var ord in cust.Orders)

  {

    //同時檢視客戶資料和訂單資料

  }

}

 

語句描述:原始查詢未請求資料,在所檢索到各個物件的連結中導航如何能導致觸發對資料庫的新查詢。

 

預先載入:LoadWith 方法

 

你如果想要同時查詢出一些物件的集合的方法。LINQ to SQL 提供了 DataLoadOptions用於立即載入物件。方法包括:

 

LoadWith 方法,用於立即載入與主目標相關的資料。

 

AssociateWith 方法,用於篩選為特定關係檢索到的物件。

 

使用 LoadWith方法指定應同時檢索與主目標相關的哪些資料。例如,如果你知道你需要有關客戶的訂單的資訊,則可以使用 LoadWith 來確保在檢索客戶資訊的同時檢索訂單資訊。使用此方法可僅訪問一次資料庫,但同時獲取兩組資訊。

 

在下面的示例中,我們通過設定DataLoadOptions,來指示DataContext 在載入Customers的同時把對應的Orders一起載入,在執行查詢時會檢索位於Sao Paulo的所有 Customers 的所有 Orders。這樣一來,連續訪問 Customer 物件的 Orders 屬性不會觸發新的資料庫查詢。在執行時生成的SQL語句使用了左連線。

 

NorthwindDataContext db = newNorthwindDataContext ();

DataLoadOptions ds = new DataLoadOptions();

ds.LoadWith<Customer>(p =>p.Orders);

db.LoadOptions = ds;

var custs = (

   from c in db2.Customers

   where c.City == "Sao Paulo"

   select c);

foreach (var cust in custs)

{

  foreach (var ord in cust.Orders)

  {

    Console.WriteLine ("CustomerID {0} has an OrderID {1}.",

       cust.CustomerID,

      ord.OrderID);

  }

}

 

語句描述:在原始查詢過程中使用 LoadWith 請求相關資料,以便稍後在檢索到的各個物件中導航時不需要對資料庫進行額外的往返。

 

LINQ to SQL語句(18)之運算子轉換

運算子轉換

1.AsEnumerable:將型別轉換為泛型 IEnumerable

 

使用 AsEnumerable<TSource> 可返回型別化為泛型 IEnumerable的引數。在 此示例中,LINQ toSQL(使用預設泛型 Query)會嘗試將查詢轉換為 SQL 並在伺服器上執行。但 where 子句引用使用者定義的客戶端方法 (isValidProduct),此方法無法轉換為 SQL。

 

解決方法是指定 where 的客戶端泛型 IEnumerable<T> 實現以替換泛型 IQueryable<T>。可通過呼叫 AsEnumerable<TSource>運算子來執行此操作。

 

var q =

  from p in db.Products.AsEnumerable()

  where isValidProduct(p)

  select p;

 

語句描述:這個例子就是使用AsEnumerable以便使用Where的客戶端IEnumerable實現,而不是預設的 IQueryable將在伺服器上轉換為SQL並執行的預設Query<T>實現。這很有必要,因為Where子句引用了使用者定義的客戶端方法isValidProduct,該方法不能轉換為SQL。

 

2.ToArray:將序列轉換為陣列

 

使用 ToArray <TSource>可從序列建立陣列。

 

var q =

  from c in db.Customers

  where c.City == "London"

   select c;

Customer[] qArray = q.ToArray();

 

語句描述:這個例子使用 ToArray 將查詢直接計算為陣列。

 

3.ToList:將序列轉換為泛型列表

 

使用 ToList<TSource>可從序列建立泛型列表。下面的示例使用 ToList<TSource>直接將查詢的計算結果放入泛型 List<T>。

 

var q =

  from e in db.Employees

  where e.HireDate >= new DateTime(1994, 1, 1)

  select e;

List<Employee> qList = q.ToList();

4.ToDictionary:將序列轉化為字典

 

使用Enumerable.ToDictionary<TSource, TKey>方法可 以將序列轉化為字典。TSource表示source中的元素的型別;TKey表示keySelector返回的鍵的型別。其返回一個包含鍵和值的Dictionary<TKey, TValue>。

 

var q =

  from p in db.Products

  where p.UnitsInStock <= p.ReorderLevel && ! p.Discontinued

  select p;

Dictionary<int, Product> qDictionary=

  q.ToDictionary(p => p.ProductID);

foreach (int key in qDictionary.Keys)

{

   Console.WriteLine(key);

}

 

語句描述:這個例子使用 ToDictionary 將查詢和鍵表示式直接鍵表示式直接計算為 Dictionary<K, T>。

 

LINQ to SQL語句(19)之ADO.NET與LINQ to SQL

ADO.NET與LINQ to SQL

 

它基於由 ADO.NET 提供程式模型提供的服務。因此,我們可以將 LINQ to SQL 程式碼與現有的 ADO.NET 應用程式混合在一起,將當前 ADO.NET 解決方案遷移到 LINQ to SQL。

 

1.連線

 

在建立 LINQ to SQL DataContext 時,可以提供現有 ADO.NET 連線。對DataContext 的所有操作(包括查詢)都使用所提供的這個連線。如果此連線已經開啟,則在您使用完此連線時,LINQ to SQL 會保持它的開啟狀態不變。我們始終可以訪問此連線,另外還可以使用 Connection 屬性自行關閉它。

 

//新建一個 標準的ADO.NET連線:

SqlConnection nwindConn = new SqlConnection(connString);

nwindConn.Open();

// ... 其它的ADO.NET資料操作 程式碼... //

//利用現有的ADO.NET連線來建立一個DataContext:

Northwind interop_db = new Northwind(nwindConn);

var orders =

   from o in interop_db.Orders

   where o.Freight > 500.00M

   select o;

//返回Freight>500.00M的訂單

nwindConn.Close();

 

語句描述:這個例子使用預先存在的ADO.NET 連線建立Northwind物件,本例中的查詢返回運費至少為500.00 的所有訂單。

 

2.事務

 

當我們已經啟動了自己的資料庫事務並且我們希望 DataContext 包含在內時,我們可以向 DataContext 提供此事務。

 

通過 .NET Framework 建立事務的首選方法是使用 TransactionScope 物件。通過使用此方法,我們可以建立跨資料庫及其他駐留在記憶體中的資源管理器執行的分散式事務。事務範圍幾乎不需要資源就可以啟動。它們僅在事務範圍記憶體在多個連線時才將自身提升為分散式事務。

 

using (TransactionScope ts = newTransactionScope())

{

  db.SubmitChanges();

  ts.Complete();

}

 

注意:不能將此方法用於所有資料庫。例如,SqlClient 連線在針對 SQL Server 2000 伺服器使用時無法提升系統事務。它採取的方法是,只要它發現有使用事務範圍的情況,它就會自動向完整的分散式事務登記。

 

下面用一個例子說明一下事務的使用方法。在這裡,也說明了重用 ADO.NET 命令和 DataContext 之間的同一連線。

 

var q =

   from p in db.Products

   where p.ProductID == 3

   select p;

//使用LINQ to SQL查詢出來

//新建一個標準的ADO.NET連線:

SqlConnection nwindConn = newSqlConnection(connString);

nwindConn.Open();

//利用現有的 ADO.NET連線來建立一個DataContext:

Northwind interop_db = newNorthwind(nwindConn);

SqlTransaction nwindTxn =nwindConn.BeginTransaction();

try

{

  SqlCommand cmd = new SqlCommand("UPDATE Products SET"

   +"QuantityPerUnit = 'single item' WHERE ProductID = 3");

  cmd.Connection = nwindConn;

   cmd.Transaction = nwindTxn;

  cmd.ExecuteNonQuery();

   interop_db.Transaction = nwindTxn;

  Product prod1 = interop_db.Products.First(p => p.ProductID == 4);

  Product prod2 = interop_db.Products.First(p => p.ProductID == 5);

   prod1.UnitsInStock -= 3;

  prod2.UnitsInStock -= 5;//這有一個錯 誤,不能為負數

  interop_db.SubmitChanges();

   nwindTxn.Commit();

}

catch (Exception e)

{

  // 如果有一個錯誤,所有的操作回滾

  Console.WriteLine (e.Message);

}

nwindConn.Close();

 

語句描述:這個例子使用預先存在的 ADO.NET 連線建立 Northwind 物件,然後與此物件共享一個 ADO.NET 事務。此事務既用於通過 ADO.NET 連線執行 SQL 命令,又用於通過 Northwind 物件提交更改。當事務因違反 CHECK 約束而中止時,將回滾所有更改,包括通過 SqlCommand 做出的更改,以及通過Northwind 物件做出的更改。

 

 

LINQ to SQL語句(20)之儲存過程

儲存過程

 

在我們編寫程式中,往往需要一些儲存過程,在LINQ to SQL中怎麼使用呢?也許比原來的更簡單些。下面我們以NORTHWND.MDF資料庫中自帶的幾個儲存過程來理解一下。

 

1.標量返回

 

在資料庫中,有名為 Customers Count By Region的儲存過程。該儲存過程返回顧客所在"WA"區域的數量。

 

ALTER PROCEDURE [dbo]. [NonRowset]

  (@param1 NVARCHAR(15))

AS

BEGIN

  SET NOCOUNT ON;

   DECLARE @count int

   SELECT @count = COUNT(*)FROM Customers

   WHERECustomers.Region = @Param1

   RETURN @count

END

 

我們只要把這個儲存過程拖到O/R設計器內,它自動生成了以下程式碼段:

 

[Function(Name = "dbo.[Customers CountBy Region]")]

public intCustomers_Count_By_Region([Parameter

(DbType = "NVarChar (15)")]string param1)

{

  IExecuteResult result = this.ExecuteMethodCall(this,

  ((MethodInfo) (MethodInfo.GetCurrentMethod())), param1);

  return ((int) (result.ReturnValue));

}

 

我們需要時,直接呼叫就可以了,例如:

 

int count = db.CustomersCountByRegion("WA");

Console.WriteLine(count);

 

語句描述:這個例項使用儲存過程返回在“WA”地區的客戶數。

 

2.單一結果集

 

從資料庫中返回行集合,幷包含用於篩選結果的輸入引數。當我們執行 返回行集合的儲存過程時,會用到結果類,它儲存從儲存過程中返回的結果。

 

下面的示例表示一個儲存過程,該儲存過程返回客戶行並使用輸入引數來僅返回將“London”列為客戶城市的那些行的固定幾列。 

 

ALTER PROCEDURE [dbo].[Customers By City]

   -- Add the parameters for the stored procedure here

   (@param1 NVARCHAR(20))

AS

BEGIN

   -- SET NOCOUNT ON added to prevent extra result sets from

   -- interfering with SELECT statements.

   SET NOCOUNT ON;

   SELECT CustomerID, ContactName, CompanyName, City from

   Customers as c where c.City=@param1

END

 

拖到O/R設計器內,它自動生成了以下程式碼段:

 

[Function(Name="dbo.[Customers ByCity]")]

publicISingleResult<Customers_By_CityResult> Customers_By_City(

[Parameter(DbType="NVarChar(20)")]string param1)

{

  IExecuteResult result = this.ExecuteMethodCall(this, (

  (MethodInfo) (MethodInfo.GetCurrentMethod())), param1);

  return ((ISingleResult<Customers_By_CityResult>)

   (result.ReturnValue));

}

 

我們用下面的程式碼呼叫:

 

ISingleResult<Customers_By_CityResult>result =

db.Customers_By_City("London");

foreach (Customers_By_CityResult cust inresult)

{

   Console.WriteLine("CustID={0}; City={1}",cust.CustomerID,

    cust.City);

}

 

語句描述:這個例項使用儲存過程返回在倫敦的客戶的 CustomerID和City。

 

3.多個可能形狀的單一結果集

 

當儲存過程可以返回多個結果形狀時,返回型別無法強型別化為單個投影形狀。儘管 LINQ to SQL 可以生成所有可能的投影型別,但它無法獲知將以何種順序返回它們。 ResultTypeAttribute 屬性適用於返回多個結果型別的儲存過程,用以指定該過程可以返回的型別的集合。

 

在下面的 SQL 程式碼示例中,結果形狀取決於輸入(param1 = 1或param1 = 2)。我們不知道先返回哪個投影。

 

ALTER PROCEDURE [dbo].[SingleRowset_MultiShape]

   -- Add the parameters for the stored procedure here

   (@param1 int )

AS

BEGIN

   -- SET NOCOUNT ON added to prevent extra result sets from

   -- interfering with SELECT statements.

   SET NOCOUNT ON;

   if(@param1 = 1)

   SELECT * from Customers as c where c.Region = 'WA'

   else if (@param1 = 2)

   SELECT CustomerID, ContactName, CompanyName from

   Customers as c where c.Region = 'WA'

END

 

拖到O/R 設計器內,它自動生成了以下程式碼段:

 

[Function (Name="dbo.[Whole Or PartialCustomers Set]")]

publicISingleResult<Whole_Or_Partial_Customers_SetResult>

Whole_Or_Partial_Customers_Set([Parameter(DbType="Int")]

System.Nullable<int> param1)

{

   IExecuteResult result = this.ExecuteMethodCall(this,

   ((MethodInfo)(MethodInfo.GetCurrentMethod())), param1);

   return ((ISingleResult<Whole_Or_Partial_Customers_SetResult>)

  (result.ReturnValue));

}

 

但是,VS2008會把多結果集儲存過程識別為單結果集的儲存過程,預設生成的程式碼我們要手動修改一下,要求返回多個結果集,像這樣:

 

[Function(Name="dbo.[Whole Or PartialCustomers Set]")]

[ResultType(typeof (WholeCustomersSetResult))]

[ResultType(typeof(PartialCustomersSetResult))]

public IMultipleResultsWhole_Or_Partial_Customers_Set([Parameter

(DbType="Int")]System.Nullable<int> param1)

{

  IExecuteResult result = this.ExecuteMethodCall(this,

   ((MethodInfo)(MethodInfo.GetCurrentMethod())), param1);

   return ((IMultipleResults)(result.ReturnValue));

}

 

我們分別定義了兩個分部類,用於指定返回的型別。WholeCustomersSetResult類 如 下:

 

public partial classWholeCustomersSetResult

{

  private string _CustomerID;

  private string _CompanyName;

  private string _ContactName;

  private string _ContactTitle;

  private string _Address;

   private string _City;

  private string _Region;

   private string _PostalCode;

  private string _Country;

  private string _Phone;

  private string _Fax;

  public WholeCustomersSetResult()

  {

  }

  [Column (Storage = "_CustomerID", DbType ="NChar(5)")]

  public string CustomerID

  {

    get { return this._CustomerID; }

    set

    {

       if ((this._CustomerID != value))

        this._CustomerID = value;

    }

  }

  [Column(Storage = "_CompanyName", DbType ="NVarChar(40)")]

  public string CompanyName

  {

    get { return this._CompanyName; }

    set

    {

       if ((this._CompanyName != value))

         this._CompanyName = value;

    }

  }

  [Column (Storage = "_ContactName", DbType ="NVarChar(30) ")]

  public string ContactName

  {

     get { return this._ContactName; }

    set

    {

      if ((this._ContactName != value))

         this._ContactName = value;

    }

  }

  [Column (Storage = "_ContactTitle", DbType ="NVarChar(30) ")]

  public string ContactTitle

  {

     get { return this._ContactTitle; }

    set

    {

      if ((this._ContactTitle != value))

         this._ContactTitle = value;

    }

  }

   [Column(Storage = "_Address", DbType = "NVarChar(60)")]

  public string Address

  {

    get { return this._Address; }

    set

    {

      if ((this._Address != value))

        this._Address = value;

    }

  }

  [Column(Storage = "_City", DbType = "NVarChar(15)")]

  public string City

  {

    get { return this._City; }

    set

    {

      if ((this._City != value))

        this._City = value;

    }

  }

  [Column(Storage = "_Region", DbType = "NVarChar(15)")]

  public string Region

  {

    get { return this._Region; }

    set

    {

      if ((this._Region != value))

        this._Region = value;

    }

  }

  [Column(Storage = "_PostalCode", DbType ="NVarChar(10)")]

   public string PostalCode

  {

    get { return this._PostalCode; }

    set

    {

       if ((this._PostalCode != value))

        this._PostalCode = value;

    }

  }

  [Column(Storage = "_Country", DbType ="NVarChar(15)")]

   public string Country

  {

    get { return this._Country; }

    set

    {

      if ((this._Country != value))

        this._Country = value;

    }

  }

  [Column(Storage = "_Phone", DbType ="NVarChar(24)")]

   public string Phone

  {

    get { return this._Phone; }

    set

    {

      if ((this._Phone != value))

        this._Phone = value;

    }

  }

  [Column(Storage = "_Fax", DbType ="NVarChar(24)")]

  public string Fax

  {

    get { return this._Fax; }

    set

    {

      if ((this._Fax != value))

        this._Fax = value;

    }

  }

}

 

PartialCustomersSetResult類 如下:

 

public partial classPartialCustomersSetResult

{

  private string _CustomerID;

  private string _ContactName;

  private string _CompanyName;

  public PartialCustomersSetResult()

  {

  }

  [Column (Storage = "_CustomerID", DbType ="NChar(5)")]

  public string CustomerID

  {

    get { return this._CustomerID; }

    set

    {

       if ((this._CustomerID != value))

        this._CustomerID = value;

    }

  }

  [Column(Storage = "_ContactName", DbType ="NVarChar(30)")]

  public string ContactName

  {

    get { return this._ContactName; }

    set

    {

       if ((this._ContactName != value))

         this._ContactName = value;

    }

  }

  [Column (Storage = "_CompanyName", DbType ="NVarChar(40) ")]

  public string CompanyName

  {

     get { return this._CompanyName; }

    set

    {

      if ((this._CompanyName != value))

         this._CompanyName = value;

    }

  }

}

 

這樣就可以使用了,下面程式碼直接呼叫,分別返回各自的結果集合。

 

//返回全部Customer結果集

IMultipleResults result =db.Whole_Or_Partial_Customers_Set(1);

IEnumerable<WholeCustomersSetResult>shape1 =

result.GetResult<WholeCustomersSetResult>();

foreach (WholeCustomersSetResult compNamein shape1)

{

   Console.WriteLine(compName.CompanyName);

}

//返回部分 Customer結果集

result =db.Whole_Or_Partial_Customers_Set(2);

IEnumerable<PartialCustomersSetResult>shape2 =

result.GetResult<PartialCustomersSetResult>();

foreach (PartialCustomersSetResult con inshape2)

{

   Console.WriteLine(con.ContactName);

}

 

語句描述:這個例項使用儲存過程返回“WA”地區中的一組客戶。返回的結果集形狀取決於傳入的引數。如果引數等於 1,則返回所有客戶屬性。如果引數等於2,則返回ContactName屬性。

 

4.多個結果集

 

這種儲存過程可以生成多個結果形狀,但我們已經知道結果的返回順序。

 

下面是一個按順序返回多個結果集的儲存過程Get Customer And Orders。 返回顧客ID為"SEVES"的顧客和他們所有的訂單。

 

ALTER PROCEDURE [dbo].[Get Customer AndOrders]

(@CustomerID nchar(5))

  -- Add the parameters for the stored procedure here

AS

BEGIN

   -- SET NOCOUNT ON added to prevent extra result sets from

  -- interfering with SELECT statements.

  SET NOCOUNT ON;

   SELECT * FROM Customers AS c WHERE c.CustomerID = @CustomerID 

  SELECT * FROM Orders AS o WHERE o.CustomerID = @CustomerID

END

 

拖到設計器程式碼如下:

 

[Function (Name="dbo.[Get Customer AndOrders]")]

publicISingleResult<Get_Customer_And_OrdersResult>

Get_Customer_And_Orders([Parameter(Name="CustomerID",

DbType="NChar(5)")] stringcustomerID)

{

   IExecuteResult result = this.ExecuteMethodCall(this,

   ((MethodInfo)(MethodInfo.GetCurrentMethod())), customerID);

   return ((ISingleResult<Get_Customer_And_OrdersResult>)

   (result.ReturnValue));

}

 

同樣,我們要修改自動生成的程式碼:

 

[Function(Name="dbo.[Get Customer AndOrders] ")]

[ResultType(typeof(CustomerResultSet))]

[ResultType(typeof(OrdersResultSet))]

public IMultipleResultsGet_Customer_And_Orders

([Parameter(Name="CustomerID",DbType="NChar(5)")]

string customerID)

{

  IExecuteResult result = this.ExecuteMethodCall(this,

  ((MethodInfo) (MethodInfo.GetCurrentMethod())), customerID);

  return ((IMultipleResults)(result.ReturnValue));

}

 

同樣,自己手寫類,讓其儲存過程返回各自的結果集。

 

CustomerResultSet類

 

public partial class CustomerResultSet

{

  private string _CustomerID;

  private string _CompanyName;

  private string _ContactName;

  private string _ContactTitle;

  private string _Address;

   private string _City;

  private string _Region;

   private string _PostalCode;

  private string _Country;

  private string _Phone;

  private string _Fax;

  public CustomerResultSet()

  {

  }

  [Column(Storage = "_CustomerID", DbType ="NChar(5)")]

   public string CustomerID

  {

    get { return this._CustomerID; }

    set

    {

       if ((this._CustomerID != value))

        this._CustomerID = value;

    }

  }

  [Column(Storage = "_CompanyName", DbType ="NVarChar(40)")]

  public string CompanyName

  {

    get { return this._CompanyName; }

    set

    {

       if ((this._CompanyName != value))

         this._CompanyName = value;

    }

  }

  [Column (Storage = "_ContactName", DbType ="NVarChar(30) ")]

  public string ContactName

  {

     get { return this._ContactName; }

    set

    {

      if ((this._ContactName != value))

         this._ContactName = value;

    }

  }

  [Column (Storage = "_ContactTitle", DbType ="NVarChar(30) ")]

  public string ContactTitle

  {

     get { return this._ContactTitle; }

    set

    {

      if ((this._ContactTitle != value))

         this._ContactTitle = value;

    }

  }

   [Column(Storage = "_Address", DbType = "NVarChar(60)")]

  public string Address

  {

    get { return this._Address; }

    set

    {

      if ((this._Address != value))

        this._Address = value;

    }

  }

  [Column(Storage = "_City", DbType ="NVarChar(15)")]

  public string City

  {

    get { return this._City; }

    set

    {

      if ((this._City != value))

        this._City = value;

    }

  }

  [Column(Storage = "_Region", DbType = "NVarChar(15)")]

  public string Region

  {

    get { return this._Region; }

    set

    {

      if ((this._Region != value))

        this._Region = value;

    }

  }

  [Column(Storage = "_PostalCode", DbType ="NVarChar(10)")]

   public string PostalCode

  {

    get { return this._PostalCode; }

    set

    {

       if ((this._PostalCode != value))

        this._PostalCode = value;

    }

  }

  [Column(Storage = "_Country", DbType ="NVarChar(15)")]

   public string Country

  {

    get { return this._Country; }

    set

    {

      if ((this._Country != value))

        this._Country = value;

    }

  }

  [Column(Storage = "_Phone", DbType ="NVarChar(24)")]

   public string Phone

  {

    get { return this._Phone; }

    set

    {

      if ((this._Phone != value))

        this._Phone = value;

    }

  }

  [Column(Storage = "_Fax", DbType ="NVarChar(24)")]

  public string Fax

  {

    get { return this._Fax; }

    set

    {

      if ((this._Fax != value))

        this._Fax = value;

    }

  }

}

 

OrdersResultSet 類

 

public partial class OrdersResultSet

{

  private System.Nullable<int> _OrderID;

  private string _CustomerID;

  private System.Nullable<int> _EmployeeID;

  private System.Nullable<System.DateTime> _OrderDate;

  private System.Nullable<System.DateTime> _RequiredDate;

  private System.Nullable<System.DateTime> _ShippedDate;

  private System.Nullable<int> _ShipVia;

  private System.Nullable<decimal> _Freight;

   private string _ShipName;

  private string _ShipAddress;

  private string _ShipCity;

  private string _ShipRegion;

  private string _ShipPostalCode;

  private string _ShipCountry;

  public OrdersResultSet()

  {

  }

  [Column(Storage = "_OrderID", DbType = "Int")]

  public System.Nullable<int> OrderID

  {

    get { return this._OrderID; }

    set

    {

      if ((this._OrderID != value))

        this._OrderID = value;

    }

  }

  [Column(Storage = "_CustomerID", DbType ="NChar(5)")]

  public string CustomerID

  {

    get { return this._CustomerID; }

    set

    {

      if ((this._CustomerID != value))

        this._CustomerID = value;

    }

  }

   [Column(Storage = "_EmployeeID", DbType ="Int")]

  public System.Nullable<int> EmployeeID

  {

    get { return this._EmployeeID; }

    set

    {

      if ((this._EmployeeID != value))

        this._EmployeeID = value;

    }

  }

   [Column(Storage = "_OrderDate", DbType ="DateTime")]

  public System.Nullable<System.DateTime> OrderDate

  {

    get { return this._OrderDate; }

    set

    {

      if ((this._OrderDate != value))

         this._OrderDate = value;

    }

  }

  [Column (Storage = "_RequiredDate", DbType ="DateTime")]

  public System.Nullable<System.DateTime> RequiredDate

  {

    get { return this._RequiredDate; }

    set

    {

      if ((this._RequiredDate != value))

         this._RequiredDate = value;

    }

  }

   [Column(Storage = "_ShippedDate", DbType ="DateTime")]

  public System.Nullable<System.DateTime> ShippedDate

  {

    get { return this._ShippedDate; }

    set

     {

      if ((this._ShippedDate != value))

        this._ShippedDate = value;

    }

  }

   [Column(Storage = "_ShipVia", DbType = "Int")]

  public System.Nullable<int> ShipVia

  {

    get { return this._ShipVia; }

    set

    {

      if ((this._ShipVia != value))

         this._ShipVia = value;

    }

  }

  [Column (Storage = "_Freight", DbType ="Money")]

  public System.Nullable<decimal> Freight

  {

    get { return this._Freight; }

    set

    {

      if ((this._Freight != value))

         this._Freight = value;

    }

  }

  [Column (Storage = "_ShipName", DbType ="NVarChar(40)")]

  public string ShipName

  {

    get { return this._ShipName; }

    set

    {

      if ((this._ShipName != value))

        this._ShipName = value;

    }

  }

  [Column(Storage = "_ShipAddress", DbType ="NVarChar(60)")]

  public string ShipAddress

  {

    get { return this._ShipAddress; }

    set

    {

       if ((this._ShipAddress != value))

         this._ShipAddress = value;

    }

  }

  [Column (Storage = "_ShipCity", DbType ="NVarChar(15)")]

  public string ShipCity

  {

    get { return this._ShipCity; }

    set

    {

      if ((this._ShipCity != value))

        this._ShipCity = value;

    }

  }

  [Column(Storage = "_ShipRegion", DbType ="NVarChar(15)")]

   public string ShipRegion

  {

    get { return this._ShipRegion; }

    set

    {

       if ((this._ShipRegion != value))

        this._ShipRegion = value;

    }

  }

  [Column(Storage = "_ShipPostalCode", DbType ="NVarChar(10)")]

  public string ShipPostalCode

  {

    get { return this._ShipPostalCode; }

    set

    {

      if ((this._ShipPostalCode != value))

         this._ShipPostalCode = value;

    }

  }

   [Column(Storage = "_ShipCountry", DbType = "NVarChar(15)")]

  public string ShipCountry

  {

    get { return this._ShipCountry; }

    set

    {

      if ((this._ShipCountry != value))

         this._ShipCountry = value;

    }

  }

}

 

 

這時,只要呼叫就可以了。

 

IMultipleResults result =db.Get_Customer_And_Orders("SEVES");

//返回 Customer結果集

IEnumerable<CustomerResultSet>customer =

result.GetResult<CustomerResultSet>();

//返回Orders結果集

IEnumerable<OrdersResultSet> orders =

result.GetResult<OrdersResultSet>();

//在這裡,我們讀取CustomerResultSet中的資料

foreach (CustomerResultSet cust incustomer)

{

  Console.WriteLine(cust.CustomerID);

}

 

語句描述:這個例項使用儲存過程返回客戶“SEVES”及其所有訂單。

 

5.帶輸出引數

 

LINQ to SQL 將輸出引數對映到引用引數 ,並且對於值型別,它將引數宣告為可以為null。

 

下面的示例帶有單個輸入引數(客戶 ID)並返回一個輸出引數(該客戶的總銷售額)。

 

ALTER PROCEDURE [dbo].[CustOrderTotal]

@CustomerID nchar(5),

@TotalSales money OUTPUT

AS

SELECT @TotalSales =SUM(OD.UNITPRICE*(1-OD.DISCOUNT) * OD.QUANTITY)

FROM ORDERS O, "ORDER DETAILS" OD

where O.CUSTOMERID = @CustomerID AND O.ORDERID= OD.ORDERID

 

把這個儲存過程拖到設計器中,圖片如下:

 

 

其生成程式碼如下:

 

[Function(Name="dbo.CustOrderTotal")]

public int CustOrderTotal (

[Parameter(Name="CustomerID",DbType="NChar(5) ")]string customerID,

[Parameter (Name="TotalSales",DbType="Money")]

 ref System.Nullable<decimal> totalSales)

{

   IExecuteResult result = this.ExecuteMethodCall(this,

   ((MethodInfo)(MethodInfo.GetCurrentMethod())),

  customerID, totalSales);

  totalSales = ((System.Nullable<decimal>)

  (result.GetParameterValue(1)));

  return ((int) (result.ReturnValue));

}

 

我們使用下面的語句呼叫此儲存過程:注意:輸出引數是按引用傳遞的,以支援引數為“in/out”的方案。在這種情況下,引數僅為“out”。

 

decimal? totalSales = 0;

string customerID = "ALFKI";

db.CustOrderTotal(customerID, reftotalSales);

Console.WriteLine("Total Sales forCustomer '{0}' = {1:C}",

customerID, totalSales);

 

語句描述:這個例項使用返回 Out 引數的儲存過程。

 

好了,就說到這裡了,其增刪改操作同理。相信大家通過這5個例項理解了儲存過程。

 

LINQ to SQL語句(21)之使用者定義函式

使用者定義函式

 

我們可以在LINQ to SQL中使用使用者定義函式。只要把使用者定義函式拖到O/R設計器中,LINQ to SQL自動使用FunctionAttribute屬性和ParameterAttribute屬性(如果需要)將其函式指定為方法。這時,我們只需簡單呼叫即可。

 

在這裡注意:使用使用者定義函式的時候必須滿足以下形式之一,否則會出現InvalidOperationException異常情況。

 

具有正確對映屬性的方法呼叫的函式。這裡使用FunctionAttribute屬性和 ParameterAttribute屬性。

 

特定於LINQ to SQL的靜態SQL方法。

 

.NET Framework方法支援的函式。

 

下面介紹幾個例子:

 

1.在Select中使用使用者定義的標量函式

 

所謂標量函式是指返回在 RETURNS 子句中定義的型別的單個資料值。可以使用所有標量資料型別,包括 bigint 和 sql_variant。不支援 timestamp 資料型別、使用者定義資料型別和非標量型別(如 table 或 cursor)。在 BEGIN...END 塊中定義的函式主體包含返回該值的 Transact-SQL 語句系列。返回型別可以是除 text、ntext、image 、cursor 和 timestamp 之外的任何資料型別。我們在系統自帶的 NORTHWND.MDF資料庫中,有3個自定義函式,這裡使用  TotalProductUnitPriceByCategory,其程式碼如下:

 

ALTER FUNCTION[dbo].[TotalProductUnitPriceByCategory]

(@categoryID int)

RETURNS Money

AS

BEGIN

  -- Declare the return variable here

  DECLARE @ResultVar Money

  -- Add the T-SQL statements to compute the return value here

  SELECT @ResultVar = (Select SUM(UnitPrice)

            from Products

            where CategoryID = @categoryID)

  -- Return the result of the function

  RETURN @ResultVar

END

 

我們將其拖到設計器中,LINQ to SQL通過使用 FunctionAttribute 屬性將類中定義的客戶端方法對映到使用者定義的函式。請注意,這個方法體會構造一個捕獲方法呼叫意向的表示式,並將該表示式傳遞給 DataContext 進行轉換和執行。

 

[Function(Name="dbo.TotalProductUnitPriceByCategory",

IsComposable=true)]

public System.Nullable<decimal>TotalProductUnitPriceByCategory(

[Parameter (DbType="Int")]System.Nullable<int> categoryID)

{

  return ((System.Nullable<decimal>)(this.ExecuteMethodCall(this,

  ((MethodInfo) (MethodInfo.GetCurrentMethod())), categoryID)

   .ReturnValue));

}

 

我們使用時,可以用以下程式碼來呼叫:

 

var q = from c in db.Categories

    select new

    {

      c.CategoryID,

       TotalUnitPrice =

         db.TotalProductUnitPriceByCategory(c.CategoryID)

    };

 

這時,LINQ to SQL自動生成SQL語句如下:

 

SELECT [t0].[CategoryID],CONVERT(Decimal(29,4),

[dbo].[TotalProductUnitPriceByCategory]([t0].[CategoryID]))

AS [TotalUnitPrice] FROM [dbo].[Categories]AS [t0]

2.在Where從句中 使用使用者定義的標量函式

這個例子使用方法同上一個例子原理基本相同了,MinUnitPriceByCategory自定義函式如下:

 

ALTER FUNCTION[dbo].[MinUnitPriceByCategory]

(@categoryID INT

)

RETURNS Money

AS

BEGIN

  -- Declare the return variable here

  DECLARE @ResultVar Money

  -- Add the T -SQL statements to compute the return value here

  SELECT @ResultVar = MIN(p.UnitPrice) FROM Products as p

  WHERE p.CategoryID = @categoryID

  -- Return the result of the function

  RETURN @ResultVar

END

 

拖到設計器中,生成程式碼如下:

 

[Function (Name="dbo.MinUnitPriceByCategory",IsComposable=true)]

public System.Nullable<decimal>MinUnitPriceByCategory(

[Parameter(DbType="Int")]System.Nullable<int> categoryID)

{

  return ((System.Nullable<decimal>) (this.ExecuteMethodCall(

  this, ((MethodInfo) (MethodInfo.GetCurrentMethod())),

   categoryID).ReturnValue));

}

 

這時可以使用了:注意這裡在 LINQ to SQL 查詢中,對生成的使用者定義函式方法 MinUnitPriceByCategory的內聯呼叫。此函式不會立即執行,這是因為查詢會延 遲執行。延遲執行的查詢中包含的函式直到此查詢執行時才會執行。為此查詢生成的 SQL 會轉換成對資料庫中使用者定義函式的呼叫(請參見此查詢後面的生成的 SQL語句),當在查詢外部呼叫這個函式時,LINQ to SQL 會用方法呼叫表示式建立一個簡單查詢並執行。 

 

var q =

  from p in db.Products

  where p.UnitPrice ==

   db.MinUnitPriceByCategory(p.CategoryID)

  select p;

 

它自動生成的SQL語句如下:

 

SELECT [t0]. [ProductID],[t0].[ProductName], [t0].[SupplierID],

[t0]. [CategoryID],[t0].[QuantityPerUnit],[t0].[UnitPrice],

[t0]. [UnitsInStock],[t0].[UnitsOnOrder],[t0].[ReorderLevel],

[t0]. [Discontinued]FROM [dbo].[Products]AS [t0]

WHERE [t0]. [UnitPrice] =

[dbo].[MinUnitPriceByCategory]([t0].[CategoryID])

 

3.使用使用者定義的表值函式

 

表值函式返回單個行集(與儲存過程不同,儲存過程可返回多個結果形狀)。由於表值函式的返回型別為 Table,因此在 SQL 中可以使用表的任何地方均可以使用表值函式。此外,您還可以完全像處理表那樣來處理表值函式。

 

下面的 SQL 使用者定義函式顯式宣告其返回一個 TABLE。因此,隱式定義了所返回的行集結構。

ALTER FUNCTION[dbo].[ProductsUnderThisUnitPrice]

(@price Money

)

RETURNS TABLE

AS

RETURN

  SELECT *

   FROM Products as P

  Where p.UnitPrice < @price

 

拖到設計器中,LINQ to SQL 按如下方式對映此函式:

 

[Function(Name="dbo.ProductsUnderThisUnitPrice",

IsComposable=true)]

publicIQueryable<ProductsUnderThisUnitPriceResult>

ProductsUnderThisUnitPrice([Parameter(DbType="Money")]

System.Nullable<decimal> price)

{

  return this.CreateMethodCallQuery

   <ProductsUnderThisUnitPriceResult>(this,

  ((MethodInfo) (MethodInfo.GetCurrentMethod())), price);

}

 

這時我們小小的修改一下Discontinued屬性為可空的bool型別。

 

private System.Nullable<bool>_Discontinued;

public System.Nullable<bool>Discontinued

{

}

 

我們可以這樣呼叫使用了:

 

var q = from p indb.ProductsUnderThisUnitPrice(10.25M)

    where ! (p.Discontinued ?? false)

    select p;

 

其生成 SQL語句如下:

 

SELECT [t0].[ProductID],[t0].[ProductName], [t0].[SupplierID],

[t0].[CategoryID], [t0].[QuantityPerUnit],[t0].[UnitPrice],

[t0].[UnitsInStock], [t0].[UnitsOnOrder],[t0].[ReorderLevel],

[t0].[Discontinued]

FROM [dbo].[ProductsUnderThisUnitPrice](@p0) AS [t0]

WHERE NOT ((COALESCE([t0].[Discontinued],@p1)) = 1)

-- @p0: Input Money (Size = 0; Prec = 19;Scale = 4) [10.25]

-- @p1: Input Int (Size = 0; Prec = 0;Scale = 0) [0]

4.以聯接方式使用使用者定義的表值函式

 

我們利用上面的ProductsUnderThisUnitPrice使用者定義函式,在 LINQ to SQL 中, 呼叫如下:

 

var q =

  from c in db.Categories

  join p in db.ProductsUnderThisUnitPrice(8.50M) on

   c.CategoryID equals p.CategoryID into prods

  from p in prods

  select new

  {

    c.CategoryID,

    c.CategoryName,

    p.ProductName,

     p.UnitPrice

  };

 

其生成的 SQL 程式碼說明對此函式返回的表執行聯接。

 

SELECT [t0].[CategoryID], [t0]. [CategoryName],

[t1].[ProductName], [t1].[UnitPrice]

FROM [dbo].[Categories] AS [t0]

CROSS JOIN [dbo].[ProductsUnderThisUnitPrice](@p0) AS [t1]

WHERE ([t0]. [CategoryID]) =[t1].[CategoryID]

-- @p0: Input Money (Size = 0; Prec = 19;Scale = 4) [8.50]

 

 

LINQ to SQL語句(22)之DataContext

DataContext

 

DataContext作為LINQ to SQL框架的主入口點,為我們 提供了一些方法和屬性,本文用幾個例子說明DataContext幾個典型的應用。

 

建立和刪除資料庫

 

CreateDatabase方法用於在伺服器上建立資料庫。

 

DeleteDatabase方法用於刪除由DataContext連線字串標識的資料 庫。

 

資料庫的名稱有以下方法來定義:

 

如果資料庫在連線字串中標識,則使用該連線字串的名稱。

 

如果存在DatabaseAttribute屬性 (Attribute),則將其Name屬性(Property)用作資料庫的名稱。

 

如果連線字串中沒有資料庫標記,並且使用強型別的DataContext,則會檢查與 DataContext繼承類名稱相同的資料庫。如果使用弱型別的DataContext,則會引發異常。

 

如果已通過使用檔名建立了DataContext,則會建立與該檔名相對應的資料庫。

 

我們首先用實體類描述關聯式資料庫表和列的結構的屬性。再呼叫DataContext的CreateDatabase方法,LINQ to SQL會用我們的定義的實體類結構來構造一個新的資料庫例項。還可以通過使用 .mdf 檔案或只使用目錄名(取決於連線字串),將 CreateDatabase與SQL Server一起使用。 LINQ to SQL使用連線字串來定義要建立的資料庫和作為資料庫建立位置的伺服器。

 

說了這麼多,用一段例項說明一下吧!

 

首先,我們新建一個NewCreateDB類用於建立一個名為NewCreateDB.mdf的新資料庫,該資料庫有一個Person表,有三個欄位,分別為PersonID、PersonName、Age。

 

public class NewCreateDB : DataContext

{

  public Table<Person> Persons;

  public NewCreateDB (string connection)

    :

    base(connection)

  {

  }

  public NewCreateDB(System.Data.IDbConnection connection)

    :

    base(connection)

  {

  }

}

[Table(Name = "Person")]

public partial class Person :INotifyPropertyChanged

{

  private int _PersonID;

  private string _PersonName;

  private System.Nullable<int> _Age;

  public Person() { }

  [Column(Storage = "_PersonID", DbType = "INT",

    IsPrimaryKey = true)]

  public int PersonID

  {

    get { return this._PersonID; }

    set

    {

      if ((this._PersonID != value))

      {

         this.OnPropertyChanged("PersonID");

         this._PersonID = value;

        this.OnPropertyChanged ("PersonID");

      }

    }

  }

  [Column(Storage = "_PersonName", DbType ="NVarChar(30)")]

  public string PersonName

   {

    get { return this._PersonName; }

    set

    {

      if ((this._PersonName != value))

      {

        this.OnPropertyChanged ("PersonName");

        this._PersonName = value;

        this.OnPropertyChanged ("PersonName");

      }

    }

  }

  [Column(Storage = "_Age", DbType = "INT")]

  public System.Nullable<int> Age

  {

    get { return this._Age; }

    set

    {

      if ((this._Age != value))

       {

        this.OnPropertyChanged("Age");

        this._Age = value;

         this.OnPropertyChanged("Age");

      }

    }

  }

  public event PropertyChangedEventHandler PropertyChanged;

  protected virtual void OnPropertyChanged (string PropertyName)

  {

    if ((this.PropertyChanged != null))

    {

       this.PropertyChanged(this,

        new PropertyChangedEventArgs(PropertyName));

    }

  }

}

 

接下來的一段程式碼先建立一個資料庫,在呼叫 CreateDatabase後,新的資料庫就會存在並且會接受一般的查詢和命令。接著插入一條記錄並且查詢。最後刪除這個資料庫。

 

//1.新建一個臨時 資料夾來存放新建的資料庫

string userTempFolder =Environment.GetEnvironmentVariable

("SystemDrive") +@"YJingLee";

Directory.CreateDirectory (userTempFolder);

//2.新建資料庫NewCreateDB

string userMDF = System.IO.Path.Combine(userTempFolder,

  @"NewCreateDB.mdf");

string connStr = String.Format (@"DataSource=.SQLEXPRESS;

AttachDbFilename={0};IntegratedSecurity=True;

Connect Timeout=30;User Instance=True;

Integrated Security = SSPI;",userMDF);

NewCreateDB newDB = newNewCreateDB(connStr);

newDB.CreateDatabase();

//3.插入 資料並查詢

var newRow = new Person

{

   PersonID = 1,

   PersonName = "YJingLee",

   Age = 22

};

newDB.Persons.InsertOnSubmit(newRow);

newDB.SubmitChanges();

var q = from x in newDB.Persons

     select x;

//4.刪除資料庫

newDB.DeleteDatabase();

//5.刪除臨時目錄

Directory.Delete (userTempFolder);

 

資料庫驗證

 

DatabaseExists方法用於 嘗試通過使用DataContext中的連線開啟資料庫,如果成功返回true。

 

下面程式碼說明是否存在Northwind資料庫和NewCreateDB資料庫。

 

// 檢測Northwind資料庫是否存在

if (db.DatabaseExists())

   Console.WriteLine("Northwind資料庫存在");

else

  Console.WriteLine("Northwind資料庫不存在");

//檢測 NewCreateDB資料庫是否存在

string userTempFolder =Environment.GetEnvironmentVariable("Temp");

string userMDF =System.IO.Path.Combine(userTempFolder,

@"NewCreateDB.mdf");

NewCreateDB newDB = newNewCreateDB(userMDF);

if (newDB.DatabaseExists())

   Console.WriteLine("NewCreateDB資料庫存在");

else

  Console.WriteLine("NewCreateDB資料庫不存在 ");

 

資料庫更改

 

SubmitChanges方法計算要插入、更 新或刪除的已修改物件的集,並執行相應命令以實現對資料庫的更改。

 

無論物件做了多少項更改,都只是在更改記憶體中的副本。並未對資料庫中的實際資料做任何更改。直到對DataContext顯式呼叫SubmitChanges,所做的更改才會傳輸到伺服器。呼叫時,DataContext會設法將我們所做的更改轉換為等效的SQL 命令。我們也可以使用自己的自定義邏輯來重寫這些操作,但提交順序是由 DataContext的一項稱作“更改處理器”的服務來協調的。事件的順序如下:

 

當呼叫SubmitChanges時,LINQ to SQL會檢查已知物件的集合以確定新例項是否已附加到它們。如果已附加,這些新例項將新增到被跟蹤物件的集合。

 

所有具有掛起更改的物件將按照它們之間的依賴關係排序成一個物件序列。如果一個物件的更改依賴於其他物件,則這個物件將排在其依賴項之後。

 

在即將傳輸任何實際更改時,LINQ to SQL會啟動一個事務來封裝由各條命令組成的系列。

 

對物件的更改會逐個轉換為SQL命令,然後傳送到伺服器。

 

如果資料庫檢測到任何錯誤,都會造成提交程式停止並引發異常。將回滾對資料庫的所有更改,就像未進行過提交一樣。DataContext 仍具有所有更改的完整記錄。

 

下面程式碼說明的是在資料庫中查詢CustomerID 為ALFKI的顧客,然後修改其公司名稱,第一次更新並呼叫SubmitChanges()方法,第二次更新了資料但並未呼叫SubmitChanges()方法。

//查詢

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

//更新資料並呼叫SubmitChanges()方法

cust.CompanyName = "YJingLee'sBlog";

db.SubmitChanges();

//更新資料沒有呼叫SubmitChanges()方法

cust.CompanyName ="http://lyj.cnblogs.com";

 

動態查詢

 

使用動態查詢,這個例子用CreateQuery()方法建立一個 IQueryable<T>型別表示式輸出查詢的語句。這裡給個例子說明一下。有關動態查詢具體內容,下一篇介紹。

 

var c1 =Expression.Parameter(typeof(Customer), "c");

PropertyInfo City =typeof(Customer).GetProperty ("City");

var pred =Expression.Lambda<Func<Customer, bool>>(

   Expression.Equal(

  Expression.Property(c1, City),

   Expression.Constant("Seattle")

  ), c1

);

IQueryable custs = db.Customers;

Expression expr =Expression.Call(typeof(Queryable), "Where",

  new Type[] { custs.ElementType }, custs.Expression, pred);

IQueryable<Customer> q =db.Customers.AsQueryable().

Provider.CreateQuery<Customer>(expr);

 

日誌

 

Log屬性用於將SQL查詢或命令列印到TextReader。此方法對了解 LINQto SQL 功能和除錯特定的問題可能很有用。

 

下面的示例使用Log屬性在 SQL程式碼執行前在控制檯視窗中顯示此程式碼。我們可以將此屬性與查詢、插入、更新和刪除命令一起使用。

 

//關閉日誌功能

//db.Log = null;

//使用日誌功能:日誌輸出到控制檯視窗

db.Log = Console.Out;

var q = from c in db.Customers

    where c.City == "London"

    select c;

//日誌輸出到 檔案

StreamWriter sw = newStreamWriter(Server.MapPath ("log.txt"), true);

db.Log = sw;

var q = from c in db.Customers

    where c.City == "London"

    select c;

sw.Close();

 

LINQ to SQL語句(23)之動態查詢

動態查詢

 

有這樣一個場景:應用程式可能會提供一個使用者介面,使用者可以使用該使用者介面指定一個或多個謂詞來篩選資料。這種情況在編譯時不知道查詢的細節,動態查詢將十分有用。

 

在LINQ中,Lambda表示式是許多標準查詢運算子的基礎,編譯器建立lambda表示式以捕獲基礎查詢方法(例如 Where、Select、Order By、Take While 以及其他方法)中定義的計算。表示式目錄樹用於針對資料來源的結構化查詢,這些資料來源實現IQueryable<T>。例如,LINQ to SQL 提供程式實現 IQueryable<T>介面,用於查詢關係資料儲存。C#和Visual Basic編譯器會針對此類資料來源的查詢編譯為程式碼,該程式碼在執行時將生成一個表示式目錄樹。然後,查詢提供程式可以遍歷表示式目錄樹資料結構,並將其轉換為適合於資料來源的查詢語言。

 

表示式目錄樹在 LINQ中用於表示分配給型別為Expression<TDelegate>的變數的Lambda表 達式。還可用於建立動態LINQ查詢。

 

System.Linq.Expressions名稱空間 提供用於手動生成表示式目錄樹的API。Expression類包含建立特定型別的表達 式目錄樹節點的靜態工廠方法,例如,ParameterExpression(表示一個已命名的參數列達式)或 MethodCallExpression(表示一個方法呼叫)。編譯器生成的表示式目錄樹的根始終在型別Expression<TDelegate>的節點中,其中 TDelegate是包含至多五個輸入引數的任何TDelegate委託;也就是說,其根節點是表示一個lambda表示式。

 

下面幾個例子描述如何使用表示式目錄樹來建立動態LINQ查詢。

 

1.Select

 

下面例子說明如何使用表示式樹依據 IQueryable 資料來源構造一個動態查詢,查詢出每個顧客的ContactName,並用GetCommand方法獲取其生成SQL語句。

 

//依據IQueryable資料 源構造一個查詢

IQueryable<Customer> custs =db.Customers;

//組建一個表示式樹來建立一個引數

ParameterExpression param =

  Expression.Parameter(typeof (Customer), "c");

//組建表示式樹:c.ContactName

Expression selector =Expression.Property(param,

  typeof (Customer).GetProperty("ContactName"));

Expression pred =Expression.Lambda(selector, param);

//組建表示式樹:Select(c=>c.ContactName)

Expression expr = Expression.Call(typeof(Queryable), "Select",

  new Type[] { typeof (Customer), typeof(string) },

  Expression.Constant(custs), pred);

//使用表示式樹來生成動態查詢

IQueryable<string> query =db.Customers.AsQueryable()

  .Provider.CreateQuery<string>(expr);

//使用GetCommand方法 獲取SQL語句

System.Data.Common.DbCommand cmd =db.GetCommand (query);

Console.WriteLine(cmd.CommandText);

 

生成的 SQL語句為:

 

SELECT [t0].[ContactName] FROM [dbo]. [Customers]AS [t0]

2.Where

 

下面一個例子是“搭建”Where用法來動態查詢城市在倫敦的顧客。

 

IQueryable<Customer> custs =db.Customers;

// 建立一個引數c

ParameterExpression param =

   Expression.Parameter(typeof(Customer), "c");

//c.City=="London"

Expression left = Expression.Property(param,

  typeof(Customer).GetProperty ("City"));

Expression right = Expression.Constant("London");

Expression filter = Expression.Equal(left,right);

Expression pred = Expression.Lambda(filter,param);

//Where(c=>c.City=="London")

Expression expr = Expression.Call(typeof(Queryable),"Where",

  new Type[] { typeof(Customer) },

  Expression.Constant(custs), pred);

//生成動態查詢

IQueryable<Customer> query =db.Customers.AsQueryable()

  .Provider.CreateQuery<Customer>(expr);

 

生成的SQL 語句為:

 

SELECT [t0].[CustomerID],[t0].[CompanyName], [t0].[ContactName],

[t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region],

[t0].[PostalCode], [t0].[Country], [t0].[Phone], [t0].[Fax]

FROM [dbo].[Customers] AS [t0] WHERE [t0].[City] = @p0

-- @p0: Input NVarChar (Size = 6; Prec = 0;Scale = 0) [London]

 

3.OrderBy本例既實現排序功能又實現了過濾功能。

 

IQueryable<Customer> custs =db.Customers;

//建立一個 引數c

ParameterExpression param =

  Expression.Parameter (typeof(Customer), "c");

//c.City=="London"

Expression left = Expression.Property(param,

   typeof(Customer).GetProperty ("City"));

Expression right = Expression.Constant("London");

Expression filter = Expression.Equal(left,right);

Expression pred = Expression.Lambda(filter,param);

//Where(c=>c.City=="London")

MethodCallExpression whereCallExpression =Expression.Call(

   typeof(Queryable), "Where",

   new Type[] { typeof(Customer) },

   Expression.Constant(custs), pred);

//OrderBy(ContactName => ContactName)

MethodCallExpression orderByCallExpression= Expression.Call(

   typeof(Queryable), "OrderBy",

   new Type[] { typeof(Customer), typeof(string) },

   whereCallExpression,

   Expression.Lambda(Expression.Property

   (param, "ContactName"), param));

//生成動態查詢

IQueryable<Customer> query =db.Customers.AsQueryable()

   .Provider.CreateQuery<Customer> (orderByCallExpression);

 

下面一張截圖顯示了怎麼動態生成動態查詢的過程

 

生成的SQL語句為:

 

SELECT [t0].[CustomerID],[t0].[CompanyName], [t0].[ContactName],

[t0].[ContactTitle], [t0].[Address],[t0].[City], [t0].[Region],

[t0].[PostalCode], [t0].[Country], [t0].[Phone],[t0].[Fax]

FROM [dbo].[Customers] AS [t0] WHERE[t0].[City] = @p0

ORDER BY [t0].[ContactName]

-- @p0: Input NVarChar (Size = 6; Prec = 0;Scale = 0) [London]

4.Union

 

下面的例子使用表示式樹動態查詢顧客和僱員同在的城市。

 

//e.City

IQueryable<Customer> custs = db.Customers;     

ParameterExpression param1 =

Expression.Parameter(typeof(Customer),"e");

Expression left1 =Expression.Property(param1,

  typeof (Customer).GetProperty("City"));

Expression pred1 = Expression.Lambda(left1,param1);

//c.City

IQueryable<Employee> employees =db.Employees;

ParameterExpression param2 =

Expression.Parameter(typeof (Employee),"c");

Expression left2 =Expression.Property(param2,

  typeof(Employee).GetProperty ("City"));

Expression pred2 = Expression.Lambda(left2,param2);

//Select(e=>e.City)

Expression expr1 =Expression.Call(typeof(Queryable), "Select",

  new Type[] { typeof(Customer), typeof(string) },

   Expression.Constant(custs), pred1);

//Select(c=>c.City)

Expression expr2 =Expression.Call(typeof(Queryable), "Select",

  new Type[] { typeof(Employee), typeof (string) },

  Expression.Constant(employees), pred2);

//生 成動態查詢

IQueryable<string> q1 =db.Customers.AsQueryable()

  .Provider.CreateQuery<string>(expr1);

IQueryable<string> q2 =db.Employees.AsQueryable()

  .Provider.CreateQuery<string>(expr2);

//並集

var q3 = q1.Union(q2);

 

生成的SQL語句為:

 

SELECT [t2].[City]

FROM (

  SELECT [t0].[City] FROM [dbo]. [Customers] AS [t0]

  UNION

  SELECT [t1].[City] FROM [dbo].[Employees] AS [t1]

  ) AS [t2]

 

LINQ to SQL語句(24)之檢視

檢視

 

我們使用檢視和使用資料表類似,只需將檢視從“伺服器資源管理器/資料庫資源管理器”拖動到O/R 設計器上,自動可以建立基於這些檢視的實體類。我們可以同運算元據表一樣來操作檢視了。這裡注意:O/R 設計器是一個簡單的物件關係對映器,因為它僅支援 1:1 對映關係。換句話說,實體類與資料庫表或檢視之間只能具有 1:1 對映關係。不支援複雜對映(例如,將一個實體類對映到多個表)。但是,可以將一個實體類對映到一個聯接多個相關表的檢視。 下面使用NORTHWND資料庫中自帶的Invoices、QuarterlyOrders  兩個檢視為例,寫出兩個範例。

 

查詢:匿名型別形式

 

我們使用下面程式碼來查詢出ShipCity 在London的發票。

 

var q =

  from i in db.Invoices

  where i.ShipCity == "London"

  select new

  {

    i.OrderID,

     i.ProductName,

    i.Quantity,

    i.CustomerName

  };

 

這裡,生成的SQL語句同使用資料表類似:

 

SELECT [t0].[OrderID], [t0].[ProductName],[t0]. [Quantity],

[t0].[CustomerName] FROM [dbo].[Invoices]AS [t0]

WHERE [t0].[ShipCity] = @p0

-- @p0: Input NVarChar (Size = 6; Prec = 0;Scale = 0) [London]

查詢:標識對映形式

 

下例查詢出每季的訂單。

 

var q =

  from qo in db.Quarterly_Orders

  select qo;

 

生成SQL語句為:

 

SELECT [t0].[CustomerID],[t0].[CompanyName], [t0]. [City],

[t0].[Country] FROM [dbo].[QuarterlyOrders] AS [t0]

 

LINQ to SQL語句(25)之繼承

繼承支援

 

LINQ to SQL 支援單表對映,其整個繼承層次結構儲存在單個資料庫表中。該表包含整個層次結構的所有可能資料列的平展聯合。(聯合是將兩個表組合成一個表的結果,組合後的表包含任一原始表中存在的行。)每行中不適用於該行所表示的例項型別的列為 null。

 

單表對映策略是最簡單的繼承表示形式,為許多不同類別的查詢提供了良好的效能特徵,如果我們要在 LINQ to SQL 中實現這種對映,必須在繼承層次結構的根類中指定屬性 (Attribute) 和屬性 (Attribute) 的屬性 (Property)。我們還可以使用O/R設計器來對映繼承層次結構,它自動生成了程式碼。

 

下面為了演示下面的幾個例子,我們在O/R設計器內設計如下圖所示的類及其繼承關係。

 

我們學習的時候還是看看其生成的程式碼吧!

 

具體設定對映繼承層次結構有如下幾步:

 

根類新增TableAttribute屬性。

 

為層次結構中的每個類新增InheritanceMappingAttribute屬性,同樣是新增到根類中。每個InheritanceMappingAttribute屬性,定義一個Code屬性和一個Type屬性。Code 屬性的值顯示在資料庫表的IsDiscriminator列中,用來指示該行資料所屬的類或子類。Type屬性值指定鍵值所表示的類或子類。

 

僅在其中一個 InheritanceMappingAttribute屬性上,新增一個IsDefault屬性用來在資料庫表 中的鑑別器值在繼承對映中不與任何Code值匹配時指定回退對映。

 

為 ColumnAttribute屬性新增一個IsDiscriminator屬性來表示這是儲存Code值的列。

 

下面是這張圖生成的程式碼的框架(由於生成的程式碼太多,我刪除了很多“枝葉”,僅僅保留了主要的框架用於指出其實質的東西):

 

[Table(Name = "dbo.Contacts")]

[InheritanceMapping(Code ="Unknown", Type = typeof (Contact),

          IsDefault = true)]

[InheritanceMapping(Code ="Employee", Type = typeof (EmployeeContact))]

[InheritanceMapping(Code ="Supplier", Type = typeof(SupplierContact))]

[InheritanceMapping(Code ="Customer", Type = typeof (CustomerContact))]

[InheritanceMapping(Code ="Shipper", Type = typeof(ShipperContact))]

public partial class Contact :

INotifyPropertyChanging,INotifyPropertyChanged

{

  [Column(Storage = "_ContactID",IsPrimaryKey = true,

  IsDbGenerated = true)]

  public int ContactID{ }

  [Column(Storage = "_ContactType",IsDiscriminator = true)]

  public string ContactType{ }

}

public abstract partial class FullContact :Contact{ }

public partial class EmployeeContact :FullContact{ }

public partial class SupplierContact :FullContact{ }

public partial class CustomerContact :FullContact{ }

public partial class ShipperContact :Contact{ }

 

1.一般形式

 

日常我們經常寫的形式,對單表查詢。

 

var cons = from c in db.Contacts

      select c;

foreach (var con in cons) {

   Console.WriteLine("Company name: {0}", con.CompanyName);

  Console.WriteLine("Phone: {0}", con.Phone);

   Console.WriteLine("This is a {0}", con.GetType());

}

 

2.OfType形式

 

這裡我僅僅讓其返回顧客的聯絡方式。

 

var cons = from c indb.Contacts.OfType<CustomerContact>()

      select c;

 

初步學習,我們還是看看生成的SQL語句,這樣容易理解。在 SQL語句中查詢了ContactType為Customer的聯絡方式。

 

SELECT [t0].[ContactType],[t0].[ContactName], [t0].[ContactTitle],

[t0].[Address],[t0].[City], [t0].[Region],[t0].[PostalCode],

[t0].[Country],[t0].[Fax],[t0].[ContactID], [t0].[CompanyName],

[t0].[Phone] FROM [dbo].[Contacts] AS [t0]

WHERE ([t0]. [ContactType] = @p0) AND([t0].[ContactType] IS NOT NULL)

-- @p0: Input NVarChar (Size = 8; Prec = 0;Scale = 0) [Customer]

 

3.IS形式

 

這個例子查詢一下發貨人的聯絡方式。

 

var cons = from c in db.Contacts

      where c is ShipperContact

      select c;

 

生成的SQL語句如下:查詢了ContactType為Shipper的聯絡方式。大致一看好像很上面的一樣,其實這裡查詢出來的列多了很多。實際上是Contacts表的全部欄位。

 

SELECT [t0].[ContactType],[t0].[ContactID], [t0]. [CompanyName],

[t0].[Phone],[t0].[HomePage], [t0].[ContactName],

[t0].[ContactTitle], [t0].[Address], [t0].[City],

[t0].[Region], [t0].[PostalCode],[t0].[Country],

[t0].[Fax],[t0].[PhotoPath], [t0].[Photo],[t0].[Extension]

FROM [dbo].[Contacts] AS [t0] WHERE([t0].[ContactType] = @p0)

AND ([t0].[ContactType] IS NOT NULL)

-- @p0: Input NVarChar (Size = 7; Prec = 0;Scale = 0) [Shipper]

 

4.AS形式

 

這個例子就通吃了,全部查詢了一番。

 

var cons = from c in db.Contacts

      select c as FullContact;

 

生成 SQL語句如下:查詢整個Contacts表。

 

SELECT [t0]. [ContactType],[t0].[HomePage], [t0].[ContactName],

[t0]. [ContactTitle],[t0].[Address],[t0].[City],

[t0].[Region], [t0]. [PostalCode],[t0].[Country],

[t0].[Fax], [t0].[ContactID],[t0].[CompanyName],

[t0].[Phone],[t0].[PhotoPath],[t0].[Photo], [t0].[Extension]

FROM [dbo].[Contacts] AS [t0]

 

5.Cast形式

 

使用Case形式查詢出在倫敦的顧客的聯絡方式。

 

var cons = from c in db.Contacts

      where c.ContactType == "Customer" &&

           ((CustomerContact)c).City == "London"

      select c;

 

生成SQL語句如下,自己可以看懂了。

 

SELECT [t0].[ContactType],[t0].[ContactID], [t0]. [CompanyName],

[t0].[Phone], [t0].[HomePage],[t0].[ContactName],

[t0].[ContactTitle], [t0].[Address],[t0].[City], [t0].[Region],

[t0].[PostalCode], [t0].[Country],[t0].[Fax], [t0].[PhotoPath],

[t0].[Photo], [t0].[Extension]FROM [dbo].[Contacts] AS [t0]

WHERE ([t0].[ContactType] = @p0) AND ([t0].[City] = @p1)

-- @p0: Input NVarChar (Size = 8; Prec = 0;Scale = 0) [Customer]

-- @p1: Input NVarChar (Size = 6; Prec = 0;Scale = 0) [London]

 

6.UseAsDefault形式

 

當插入一條記錄時,使用預設的對映關係了,但是在查詢時,使用繼承的關係了。具體看看生成的SQL 語句就直截了當了。

 

//插入一條資料預設使用正常的對映關係

Contact contact = new Contact()

{

  ContactType = null,

  CompanyName = "Unknown Company",

   Phone = "333-444-5555"

};

db.Contacts.InsertOnSubmit(contact);

db.SubmitChanges();

//查詢一條資料預設使用繼承對映關係

var con =

  (from c in db.Contacts

   where c.CompanyName == "Unknown Company" &&

              c.Phone == "333-444-5555"

   select c).First();

 

生成SQL語句如下:

 

INSERT INTO [dbo].[Contacts]([ContactType], [CompanyName],

[Phone]) VALUES (@p0, @p1, @p2)

SELECT TOP (1) [t0].[ContactType], [t0].[ContactID],

[t0]. [CompanyName],[t0].[Phone],[t0].[HomePage],

[t0].[ContactName], [t0].[ContactTitle],[t0].[Address],

[t0].[City],[t0].[Region],[t0].[PostalCode], [t0].[Country],

[t0].[Fax], [t0].[PhotoPath], [t0].[Photo],[t0].[Extension]

FROM [dbo].[Contacts] AS [t0]

WHERE ([t0].[CompanyName] = @p0) AND([t0].[Phone] = @p1)

-- @p0: Input NVarChar (Size = 15; Prec =0; Scale = 0)

  [Unknown Company]

-- @p1: Input NVarChar (Size = 12; Prec =0; Scale = 0)

  [333-444-5555]

 

7.插入新的記錄

 

這個例子說明如何插入發貨人的聯絡方式的一條記錄。

 

//

 

1.在插入之前查詢一下,沒有資料

var ShipperContacts =

  from sc in db.Contacts.OfType<ShipperContact>()

  where sc.CompanyName == "Northwind Shipper"

  select sc;

//

 

2.插入資料

ShipperContact nsc = new ShipperContact()

{

  CompanyName = "Northwind Shipper",

  Phone = "(123)-456-7890"

};

db.Contacts.InsertOnSubmit(nsc);

db.SubmitChanges();

//

 

3.查詢資料,有一條記錄

ShipperContacts =

  from sc in db.Contacts.OfType<ShipperContact>()

   where sc.CompanyName == "Northwind Shipper"

  select sc;

//

 

4.刪除記錄

db.Contacts.DeleteOnSubmit (nsc);

db.SubmitChanges();

 

生成SQL語句如下:

 

SELECT COUNT(*) AS [value] FROM[dbo].[Contacts] AS [t0]

WHERE ([t0].[CompanyName] = @p0) AND([t0].[ContactType] = @p1)

AND ([t0].[ContactType] IS NOT NULL)

-- @p0: Input NVarChar [Northwind Shipper]

-- @p1: Input NVarChar [Shipper]

INSERT INTO [dbo].[Contacts]([ContactType],[CompanyName], [Phone])

VALUES (@p0, @p1, @p2)

-- @p0: Input NVarChar [Shipper]

-- @p1: Input NVarChar [NorthwindShipper]

-- @p2: Input NVarChar [(123)-456-7890]

SELECT COUNT(*) AS [value] FROM[dbo].[Contacts] AS [t0]

WHERE ([t0].[CompanyName] = @p0) AND([t0].[ContactType] = @p1)

AND ([t0].[ContactType] IS NOT NULL)

-- @p0: Input NVarChar [Northwind Shipper]

-- @p1: Input NVarChar [Shipper]

DELETE FROM [dbo].[Contacts] WHERE ([ContactID]= @p0) AND

([ContactType] = @p1) AND ([CompanyName] =@p2) AND ([Phone] = @p3)

-- @p0: Input Int [159]

-- @p1: Input NVarChar [Shipper]

-- @p2: Input NVarChar [NorthwindShipper]

-- @p3: Input NVarChar [(123)-456-7890]

-- @p4: Input NVarChar [Unknown]

-- @p5: Input NVarChar (Size = 8; Prec = 0;Scale = 0) [Supplier]

-- @p6: Input NVarChar (Size = 7; Prec = 0;Scale = 0) [Shipper]

-- @p7: Input NVarChar (Size = 8; Prec = 0;Scale = 0) [Employee]

-- @p8: Input NVarChar (Size = 8; Prec = 0;Scale = 0) [Customer]

轉載於 :http://www.cnblogs.com/gates/p/3784376.html   學習用

轉載於:https://www.cnblogs.com/NotEnough/p/8690407.html

相關文章