c#LINQ to SQL語句
阿新 • • 發佈:2019-01-19
LINQ to SQL語句(1)之Where
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的僱員。第二個例子使用Nullab
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的僱員。第二個例子使用Nullab