LINQ綜合學習大全(純屬轉載)

LINQ體驗html

LINQ體驗(1)——Visual Studio 2008新特性web

1、寫本系列的目的sql

我平時利用課餘零碎時間來學習ASP.NET3.五、LINQ、Silverlight、ASP.NET 3.5 Extensions等新東西,經過筆記形式來記錄本身所學的歷程,也給你們一塊兒學習Visual Studio 2008 和 .NET 3.5提供一個平臺,爲保證此係列的完整性,我打算先依次介紹一下C# 3.0新語言特性和改進,而後從一條一條LINQ語句分析來貫穿LINQ的知識點。最後經過一個實例程序實戰Linq to sql帶來全新的編程體驗。此係列比較長,歡迎你們關注和對此係列提出建議。已經熟悉的朋友歡迎去博客園社區Web技術聯盟小組,在 Web技術聯盟小組的 TerryLee組長帶領組員正在作一個基於ASP.NET 3.5和ASP.NET 3.5 Extensions的我的站點示例的開源項目Extended Personal Site Starter Kit。能夠去參與這個有意義的項目。本人剛剛學習就把本身學到的東西分享給你們吧!因本人水平有限,錯誤再所 不免,還請你們指出並諒解。數據庫

好了,讓咱們開始吧!編程

2、Visual Studio 2008 和.NET 3.5整體認識數組

在2007年11月19日,微軟發佈了Visual Studio 2008 和.NET 3.5,具體見這裏。你能夠在這裏下載Visual Studio Team Suite 2008的90天免費試用版本。緩存

.NET Framework 3.5整體框架圖
服務器

VS 2008 & .NET 3.5框架圖
數據結構

經過這兩張圖相信你們對VS2008有了一點感性的認識了。下面正式開始了。架構

3、Visual Studio 2008的新特性

本篇是此係列的第一篇,仍是從Visual Studio 2008的新特性開始吧!先整體認識一下Microsoft 花了3年時間開發出來這個新工具!

在VS2008中包含成堆的新特性和新功能,這些全新的功能會大大的提升咱們工做效率而且會減小咱們的複雜程序。主要包括.NET Framework 對重定向的支持;ASP.NET AJAX和JavaScript智能客戶端支持;全新的Web開發新體驗:Web設計器提供了分割視圖編輯、嵌套母板頁、以及強大的CSS編輯器集成;編程語言方面的改進和LINQ;瀏覽.NET Framework庫源碼;智能部署ClickOnce;.NET Framework 3.5 加強功能;集成對 Office (VSTO)和Sharepoint 2007開發的支持;在Windows Server 2008, Windows Vista 和Microsoft Office 2007下最好的開發工具集;單元測試功能,全部的Visual Studio專業版本都支持單元測試功能等等。

下面簡單介紹一下:

.NET Framework 對重定向的支持

使用Visual Studio 2008能夠進行基於多個.net framework 版本的開發,Visual Studio 2008同時支持framework 2.0/3.0和3.5幾個版本。在不一樣的版本下它能夠自動的框架特性工具箱,項目類型,引用,智能提示……過濾功能。咱們升級到Visual Studio 2008也很是簡單。

ASP.NET AJAXJavaScript智能客戶端支持

ASP.NET AJAX成爲.NET 3.5的一部分,除了包括ASP.NET AJAX 1.0的全部功能外,還集成了WebParts的UpdatePanel,與<asp:menu > and < asp:treeview>這樣的控件的ASP.NET AJAX 集成,WCF對JSON的支持,編寫JavaScript提供了智能提示的功能。

全新的Web開發新體驗

Web設計器提供了分割視圖編輯、嵌套母板頁、以及強大的CSS編輯器集成。
ASP.NET還提供了三個新的控件:<asp:ListView>、<asp:DataPager>、<asp:LinqDataSource>,這些控件對數據場景提供了很是好的支持,容許對輸出的標識作徹底的控制。

編程語言方面的改進和LINQ

VS 2008中新的VB和C#編譯器對這些語言作了顯著的改進。二者都添加了對函數式編程概念的支持。
很是酷的LINQ(語言級集成查詢),我想這也是Visual Studio 2008的賣點。

瀏覽.NET Framework庫源碼

Visual Studio 2008有內置的調試器支持,自動按需調試進入代碼(VS 2008能夠自動爲你下載適當的.NET框架庫文件)。

LINQ體驗(2)——C# 3.0新語言特性和改進(上篇)

整體來講,Visual Studio 2008和.NET 3.5是創建在.NET2.0核心的基礎之上,.NET2.0核心自己將再也不變化(若是不瞭解.NET2.0的朋友,請參看MSDN或者一些經典的書籍),C# 3.0新語言特性在.NET2.0基礎上進行了改進,這些改進的功能能夠大大簡化咱們編寫程序。關於C# 3.0新語言特性在博客園裏的不少朋友都介紹了,我在這裏簡單介紹一下,記錄本身所學的東西,也爲後面的LINQ打下基礎。

C# 3.0新語言特性和改進包括:

  • 自動屬性(Auto-Implemented Properties)
  • 隱含類型局部變量(Local Variable Type Inference)
  • 匿名類型(Anonymous Types)
  • 對象與集合初始化器(Object and Collection Initializers)
  • 擴展方法(Extension Methods)
  • Lambda表達式和Lambda表達式樹 (Lambda Expression and Lambda Expression Trees)

自動屬性(Auto-Implemented Properties)

自動屬性能夠避免原來這樣咱們手工聲明一個私有成員變量以及編寫get/set邏輯,在VS2008中能夠像下面這樣編寫一個類,編譯器會自動地生成私有變量和默認的get/set 操做。你也能夠分別定義get和set的「protected」等訪問級別。

在.Net2.0框架下,咱們能夠這樣寫一個User類:

public class User

{

    private int _id;

    private string _name;

    private int _age;

    public int Id

    {

        get { return _id; }

        set { _id = value; }

    }

    public string Name

    {

        get { return _name; }

        set { _name = value; }

    }

    public int Age

    {

        get { return _age; }

        set { _age = value; }

    }

}

如今,能夠這樣簡化:

public class User

{

    public int Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

}

像上面這樣的空的get/set屬性的話,它會自動爲你在類中生成一個私有成員變量,對這個變量實現一個公開的getter 和setter。咱們可使用.NET開發環境所提供的ildasm.exe(IL代碼反彙編器)工具來分析程序集或者模塊的內容。我就不貼圖了。

隱含類型局部變量(Local Variable Type Inference)

C#3.0引進了var這個新關鍵字,在聲明局部變量時可用於替代原先的類型名,即當一個變量聲明標識爲var類型而且該範圍域中沒有var名稱類型存在,那麼這個聲明就稱爲隱含類型局部變量。以下(等同於//後面的顯式聲明):

var i = 5;//int

var j = 23.56;//double

var k = "C Sharp";//string

var x;//錯誤

var y = null;//錯誤

var z = { 1, 2, 3 };//錯誤

在調試狀態下,編譯器解釋以下

 

隱含類型局部變量要點

  1. var爲關鍵字,能夠根據後面的初始化語句自動推斷類型,這個類型爲強類型。
  2. 初始化語句必須爲表達式,不能夠爲空。且編譯時能夠推斷類型。一旦初始化以後,只能夠存儲這種類型。
  3. var聲明的僅限於局部變量,不可用於字段。亦能夠用於for,foreach,using 等語句中。
  4. 數組也能夠做爲隱含類型。
  5. 初始化語句不能是一個自身的對象或者集合初始化器,可是他能夠是包含一個對象或者初始化器的一個new表達式。
  6. 若是局部變量聲明包含了多個聲明符,其類型必須相同。

匿名類型(Anonymous Types)

匿名類型容許定義行內類型,無須顯式定義類型。常和var配合使用來聲明匿名類型。

var p1 = new { Id = 1, Name = "YJingLee", Age = 22 };//屬性也不須要申明

var p2 = new { Id = 2, Name = "XieQing", Age = 25 };

p1 = p2;//p1,p2結構相同,能夠互相賦值

 

在這裏編譯器會認爲p1,p2至關於:

public class SomeType

{

    public int Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

}

那麼數組怎麼定義呢?使用"new[]"關鍵字來聲明數組,加上數組的初始值列表。像這樣:

var intArray = new[] { 2, 3, 5, 6 };

var strArray = new[] { "Hello", "World" };

var anonymousTypeArray = new[]

{

    new { Name = "YJingLee", Age = 22 },

    new { Name = "XieQing", Age = 25 }

};

var a = intArray[0];

var b = strArray[0];

var c = anonymousTypeArray[1].Name;

 

匿名類型要點

  1. 可使用new關鍵字調用匿名初始化器建立一個匿名類型的對象。
  2. 匿名類型直接繼承自System. Object。
  3. 匿名類型的成員是編譯器根據初始化器推斷而來的一些讀寫屬性。

對象與集合初始化器(Object and Collection Initializers)

對象初始化器 (Object Initializers) :

.NET2.0框架中的類型很是依賴於屬性。當生成對象實例和使用新的類型時,在.Net2.0時候咱們像這樣寫:

User user = new User();

user.Id = 1;

user.Name = "YJingLee";

user.Age = 22;

在VS2008中,編譯器會自動地生成合適的屬性setter代碼,使得原來幾行的屬性賦值操做能夠在一行完成。咱們能夠這樣簡化:像這樣,對象初始化器由一系列成員對象組成,其對象必須初始化,用逗號間隔,使用{}封閉。

User user = new User { Id = 1, Name = "YJingLee", Age = 22 };

又例如,我把二我的加到一個基於泛型的類型爲User的List集合中:

List<User> user = new List<User>{

    new User{Id=1,Name="YJingLee",Age=22},

    new User{Id=2,Name="XieQing",Age=25},

};

 

若是有相同名字和類型的兩個對象初始化器將會產生相同的實例,能夠相互賦值。例如:

User user = new User { Id = 1, Name = "YJingLee", Age = 22 };

User user2 = new User { Id = 2, Name = "XieQing", Age = 25 };

user = user2;

除了在初始化類時設置簡單的屬性值外,對象初始化器特性也容許咱們設置更復雜的嵌套(nested)屬性類型。例如咱們能夠在上面定義的User類型同時擁有一個屬於Address類型的叫「Address」的屬性:

User user = new User

{

    Id = 1,

    Name = "YJingLee",

    Age = 22,

    Address = new Address

    {

        City = "NanJing",

        Zip = 21000

    }

};

集合初始化器(Collection Initializers):

集合初始化器由一系列集合對象組成,用逗號間隔,使用{}封閉。
集合初始化器能夠簡化把幾個對象一塊兒添加到一個集合,編譯器會自動爲你作集合插入操做。例如我把七個數加到一個基於泛型的類型爲int的List集合中

List<int> num = new List<int> { 0, 1, 2, 6, 7, 8, 9 };

對象與集合初始化器要點

  1. 對象初始化器實際上利用了編譯器對對象中對外可見的字段和屬性進行按序賦值。
  2. 對象初始化器容許只給一部分屬性賦值,包括internal訪問級別
  3. 對象初始化器能夠結合構造函數一塊兒使用,而且構造函數初始化先於對象初始化器執行。
  4. 集合初始化器會對初始化器中的元素進行按序調用ICollection<T>.Add(T)方法。
  5. 注意對象初始化器和集合初始化器中成員的可見性和調用順序。
  6. 對象與集合初始化器一樣是一種編譯時技術。

LINQ體驗(3)——C# 3.0新語言特性和改進(下篇)

擴展方法(Extension Methods)

每每咱們須要對CLR類型進行一些操做,但苦於沒法擴展CLR類型的方法,只能建立一些helper方法,或者繼承類。咱們來修改上面的User類:

public class User

{

    public int Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

    public string Read()

    {

        return "Id:" + Id + "姓名:" + Name + "年齡:" + Age;

    }

}

而後調用

var user = new { Id = 1, Name = "YJingLee", Age = 22 };

var str = user.Read();

如今有了擴展方法就方便多了。

擴展方法容許開發人員往一個現有的CLR類型的公開契約(contract)中添加新的方法,而不用生成子類或者從新編譯原來的類型。擴展方法有助於把今天動態語言中流行的對duck typing的支持之靈活性,與強類型語言之性能和編譯時驗證融合起來。——引用Scott博文

擴展方法是能夠經過使用實例方法語法調用的靜態方法。效果上,使得附加的方法擴展已存在類型和構造類型成爲可能。他能夠對現有類功能進行擴充,從而使該類型的實例具備更多的方法(功能)。
擴展方法容許咱們在不改變源代碼的狀況下擴展(即添加不能修改)現有類型中的實例方法。

擴展方法給咱們一個怎樣的思路呢?咱們一步一步作一下!
首先聲明擴展方法:經過指定關鍵字this修飾方法的第一個參數。注意擴展方法僅可聲明在靜態類中。擴展方法具有全部常規靜態方法的全部能力,可使用實例方法語法來調用。接着就能夠調用擴展方法了。下面經過一個具體的實例分析一下:
例如咱們要檢查一個字符串變量是不是合法的電子郵件地址?在.Net2.0框架下像這樣:

var email = "leeyongjing@gmail.com";

if (EmailValidator.IsValid(email))

{

    Response.Write("YJingLee提示:這是一個正確的郵件地址");

}

而使用擴展方法的話,我能夠添加「IsValidEmailAddress()」方法到string類自己中去,該方法返回當前字符串實例是不是個合法的字符串。

if (email.IsValidEmailAddress())

{

    Response.Write("YJingLee提示:這是一個正確的郵件地址");

}

咱們是怎麼把這個IsValidEmailAddress()方法添加到現有的string類裏去的呢?先定義一個靜態類,再定義「IsValidEmailAddress」這個靜態的法來實現的。

public static class Extensions//靜態類

{

    public static bool IsValidEmailAddress(this string s)

    //靜態方法和this

    {

        Regex regex = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$");

        return regex.IsMatch(s);

    }

}

注意,上面的靜態方法在第一個類型是string的參數變量前有個「this」關鍵詞,這告訴編譯器,這個特定的擴展方法應該添加到類型爲「string」的對象中去。而後在IsValidEmailAddress()方法實現裏,我能夠訪問調用該方法的實際string實例的全部公開屬性/方法/事件,取決於它是不是合法電子郵件地址來返回true/false。

擴展方法不只可以應用到個別類型上,也能應用到.NET框架中任何基類或接口上。便可用於整個.NET框架豐富的可組合的框架層擴展。

擴展方法要點

  1. 擴展方法的本質爲將實例方法調用在編譯期改變爲靜態類中的靜態方法調用。事實上,它確實擁有靜態方法所具備的全部功能。
  2. 擴展方法的做用域是整個namespace可見的,而且能夠經過using namespace來導入其它命名空間中的擴展方法。
  3. 擴展方法的優先級:現有實例方法優先級最高,其次爲最近的namespace下的靜態類的靜態方法,最後爲較遠的namespace下的靜態類的靜態方法。
  4. 擴展方法是一種編譯時技術,注意與反射等運行時技術進行區別,並慎重使用。

Lambda表達式和Lambda表達式樹 (Lambda Expression and Lambda Expression Trees)

Lambda表達式

咱們從「全部字符串查找包含YJingLee子字符串」提及。在C# 2.0中,匿名方法容許咱們之內聯的方式來實現委託實例,它提供強大的函數式編程語言,可是標記顯得至關的冗長和帶有強制性。咱們使用C# 2.0 中的匿名方法查找,代碼以下:

var inString = list.FindAll(delegate(string s)

{ return s.Indexof("YJingLee") >= 0; });

如今可使用C# 3.0帶來的Lambda表達式容許咱們使用一種更接近人的思惟、更天然的方式來實現相似於匿名方法一樣的效果,看下面的代碼多麼簡潔:

var inString = list.FindAll(s => s.Indexof("YJingLee") >= 0);

Lambda表達式格式:(參數列表)=>表達式或語句塊
具體意義:定義Lambda接受參數列表,運行表達式或語句塊返回表達式或語句塊的值傳給這個參數列表。

Lambda表達式參數類型能夠是隱式類型或顯式類型。在顯式列表中,每一個參數的類型是顯式指定的,在隱式列表中,參數的類型由Lambda表達式出現的語境自動推斷類型。
Lambda表達式的參數列表能夠有一個或多個參數,或者無參數。在有單一的隱型參數的lambda表達式中,圓括號能夠從參數列表中省略。
例如:

(x, y) => x * y;//多參數,隱式類型=>表達式

x => x * 10;//單參數,隱式類型=>表達式

x => { return x * 10; }; //單參數,隱式類型=>語句塊

(int x) => x * 10;//單參數,顯式類型=>表達式

(int x) => { return x * 10; };//單參數,顯式類型=>語句塊

() => Console.WriteLine(); //無參數

下面看這個例子:
在前面的帖子中,咱們寫了一個User類及增長了2我的,接下來,咱們使用由LINQ提供的新的Where和Average方法來返回集合中的人的一個子集,以及計算這個集合中的人的平均年齡:

List<User> user = new List<User>{

    new User{Id=1,Name="YJingLee",Age=22},

    new User{Id=2,Name="XieQing",Age=25},

};

//獲取特定人時所用的過濾條件,p參數屬於User類型

var results = user.Where(p => p.Name == "YJingLee").ToList();

//用User對象的Age值計算平均年齡

var average = user.Average(p => p.Age);

效果圖以下:

 

對這個Lambda表達式作個簡要分析:

var resultsdelegate = user.Where(delegate(User p)

{

    return p.Name == "YJingLee";// 返回一個布爾值

});

var averagedelegate = user.Average(delegate(User p)

{

    return p.Age;

});

 

Lambda表達式L能夠被轉換爲委託類型D,須要知足如下條件:
L的參數類型要與D的參數個數相等,類型相同,返回類型相同,不管是表達式,仍是語句塊。注意隱式類型要參與類型辨析。

Lambda表達式樹

Lambda表達式樹容許咱們像處理數據(好比讀取,修改)同樣來處理Lambda表達式。我以一個例子簡單說明:

Expression<Func<int, bool>> filter = n => (n * 3) < 5;

BinaryExpression lt = (BinaryExpression)filter.Body;

BinaryExpression mult = (BinaryExpression)lt.Left;

ParameterExpression en = (ParameterExpression)mult.Left;

ConstantExpression three = (ConstantExpression)mult.Right;

ConstantExpression five = (ConstantExpression)lt.Right;

var One = filter.Compile();

Console.WriteLine("Result: {0},{1}", One(5), One(1));

Console.WriteLine("({0} ({1} {2} {3}) {4})", lt.NodeType,

         mult.NodeType, en.Name, three.Value, five.Value);

效果圖以下:

 

Lambda表達式和Lambda表達式樹要點

  1. Lambda表達式的參數類型能夠忽略,由於能夠根據使用的上下文進行推斷。
  2. Lambda表達式的主體(body)能夠是表達式,也能夠是語句塊。
  3. Lambda表達式傳入的實參將參與類型推斷,以及方法重載辨析。
  4. Lambda表達式和表達式體能夠被轉換爲表達式樹。
  5. 表達式樹容許lambda表達式可以表明數據結構替表明示爲執行代碼。

好了,我在這裏簡單的把C# 3.0新語言特性和改進說了一下,接下來,正式進入這個系列的主題部分——LINQ。爲 了讓你們瞭解,我換一種手法來寫,從一條一條LINQ to SQL語句分析來貫穿LINQ的知識點。一塊兒體驗LINQ帶給咱們的樂趣。

LINQ體驗(4)——LINQ簡介和LINQ to SQL語句之Where

查詢表達式(LINQ)簡介

在上兩篇我介紹了C#3.0新語特性和改進,這些新特性在咱們編寫程序時爲咱們提供了很是大的幫助。從這篇開始,咱們開始一塊兒來探討LINQ。

LINQ是Language Integrated Query的簡稱,它是集成在.NET編程語言中的一種特性。已成爲編程語言的一個組成部分,在編寫程序時能夠獲得很好的編譯時語法檢查,豐富的元數據,智能感知、靜態類型等強類型語言的好處。而且它同時還使得查詢能夠方便地對內存中的信息進行查詢而不只僅只是外部數據源。

LINQ定義了一組標準查詢操做符用於在全部基於.NET平臺的編程語言中更加直接地聲明跨越、過濾和投射操做的統一方式,標準查詢操做符容許查詢做用於全部基於IEnumerable<T>接口的源,而且它還容許適合於目標域或技術的第三方特定域操做符來擴大標準查詢操做符集,更重要的是,第三方操做符能夠用它們本身的提供附加服務的實現來自由地替換標準查詢操做符,根據LINQ模式的習俗,這些查詢喜歡採用與標準查詢操做符相同的語言集成和工具支持。

咱們來整體看看LINQ架構

 

在.NET3.5下,微軟爲咱們提供了一些命名空間

 

LINQ包括五個部分:LINQ to Objects、LINQ to DataSets、LINQ to SQL、LINQ to Entities、LINQ to XML。

LINQ to SQL全稱基於關係數據的.NET語言集成查詢,用於以對象形式管理關係數據,並提供了豐富的查詢功能。其創建於公共語言類型系統中的基於SQL的模式定義的集成之上,當保持關係型模型表達能力和對底層存儲的直接查詢評測的性能時,這個集成在關係型數據之上提供強類型。

LINQ to XML在System.Xml.LINQ命名空間下實現對XML的操做。採用高效、易用、內存中的XML工具在宿主編程語言中提供XPath/XQuery功能等。

說了這麼多,咱們仍是用一個簡單的實例說明一下LINQ帶來的體驗。

第一步:創建dbml(Database Mark Language。數據庫描述語言,是一種xml格式的文檔,用來描述數據庫)文件,以Northwind數據庫爲例,上述Customers類被映射成一個表,對應數據庫中的 Customers表

 

第二步:建立一個ASP.NET頁面,在頁面上加入一個GridView控件

 

第三步:編寫代碼進行數據綁定

 

第四步:運行顯示結果。

 

好了,就說這麼多吧,你們應該對LINQ有了整體的瞭解。最後我對LINQ還有一點疑惑,在此提出,請熟悉的朋友來探討:

  1. LINQ是在ADO.NET之上的,那麼在未來它會代替ADO.NET嗎?
  2. 在大型項目中使用LINQ,它的效率如何呢?

接下來,咱們開始從LINQ to SQL語句入手,來全面瞭解一下LINQ,就從最簡單的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體驗(5)——LINQ to SQL語句之Select/Distinct和Count/Sum/Min/Max/Avg

上一篇講述了LINQ,順便說了一下Where操做,這篇開始咱們繼續說LINQ to SQL語句,目的讓你們從語句的角度瞭解LINQ,LINQ包括LINQ to Objects、LINQ to DataSets、LINQ to SQL、LINQ to Entities、LINQ to XML,可是相對來講LINQ to SQL在咱們程序中使用最多,畢竟全部的數據都要在數據庫運行着各類操做。因此先來學習LINQ to SQL,其它的都差很少了,那麼就從Select提及吧,這個在編寫程序中也最爲經常使用。本篇詳細說明一下Select和Count/Sum/Min/Max/Avg。

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的名字。例如string c = "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(string Country, 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();

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

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語句

Where

過濾;延遲

Select

選擇;延遲

Distinct

查詢不重複的結果集;延遲

Count

返回集合中的元素個數,返回INT類型;不延遲

LongCount

返回集合中的元素個數,返回LONG類型;不延遲

Sum

返回集合中數值類型元素之和,集合應爲INT類型集合;不延遲

Min

返回集合中元素的最小值;不延遲

Max

返回集合中元素的最大值;不延遲

Average

返回集合中的數值類型元素的平均值。集合應爲數字類型集合,其返回值類型爲double;不延遲

Aggregate

 

LINQ體驗(6)——LINQ to SQL語句之Join和Order By

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語句寫成select new { e.FirstName, e.LastName, Order = ords }的話,那麼Order就是一個集合。

4.投影的Let賦值(Projected let 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

    };

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 =

    db.Customers

    .OrderBy(c => c.City)

    .ThenBy(c => c.ContactName).ToList();

在T-SQL中沒有ThenBy語句,其依然翻譯爲OrderBy,因此也能夠用下面語句來表達:

var q =

    db.Customers

    .OrderBy(c => c.ContactName)

    .OrderBy(c => c.City).ToList();

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

var q =

    db.Customers

    .OrderByDescending(c => c.City)

    .ThenByDescending(c => c.ContactName).ToList();

須要說明的是,OrderBy操做,不支持按type排序,也不支持匿名類。好比

var q =

    db.Customers

    .OrderBy(c => new

    {

        c.City,

        c.ContactName

    }).ToList();

會被拋出異常。錯誤是前面的操做有匿名類,再跟OrderBy時,比較的是類別。好比

var q =

    db.Customers

    .Select(c => new

    {

        c.City,

        c.Address

    })

    .OrderBy(c => c).ToList();

若是你想使用OrderBy(c => c),其前提條件是,前面步驟中,所產生的對象的類別必須爲C#語言的基本類型。好比下句,這裏City爲string類型。

var q =

    db.Customers

    .Select(c => c.City)

    .OrderBy(c => c).ToList();

5.ThenByDescending

這兩個擴展方式都是用在OrderBy/OrderByDescending以後的,第一個ThenBy/ThenByDescending擴展方法做爲第二位排序依據,第二個ThenBy/ThenByDescending則做爲第三位排序依據,以此類推

var q =

    from o in db.Orders

    where o.EmployeeID == 1

    orderby o.ShipCountry, o.Freight descending

    select o;

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

6.帶GroupBy形式

var q =

    from p in db.Products

    group p by p.CategoryID into g

    orderby g.Key

    select new {

        g.Key,

        MostExpensiveProducts =

            from p2 in g

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

            select p2

    };

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

LINQ體驗(7)——LINQ to SQL語句之Group By/Having和Exists/In/Any/All/Contains

咱們繼續講解LINQ to SQL語句,這篇咱們來討論Group By/Having操做符和Exists/In/Any/All/Contains操做符。

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分類。其結果分爲兩類,大於的是一類,小於及等於爲另外一類。

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查找其所在城市爲西雅圖、倫敦、巴黎或溫哥華的客戶。

總結一下這篇咱們說明了如下語句:

Group By/Having

分組數據;延遲

Any

用於判斷集合中是否有元素知足某一條件;不延遲

All

用於判斷集合中全部元素是否都知足某一條件;不延遲

Contains

用於判斷集合中是否包含有某一元素;不延遲

LINQ體驗(8)——LINQ to SQL語句之Union All/Union/Intersect和Top/Bottom和Paging和SqlMethods

咱們繼續講解LINQ to SQL語句,這篇咱們來討論Union All/Union/Intersect操做和Top/Bottom操做和Paging操做和SqlMethods操做 。

Union All/Union/Intersect操做

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

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

        );

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

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 = new NorthwindDataContext();

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體驗(9)——LINQ to SQL語句之Insert/Update/Delete操做

咱們繼續講解LINQ to SQL語句,這篇咱們來討論Insert/Update/Delete操做。這個在咱們的程序中最爲經常使用了。咱們直接看例子。

Insert/Update/Delete操做

插入(Insert)

1.簡單形式

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

NorthwindDataContext db = new NorthwindDataContext();
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 = new EmployeeTerritory
{
    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重寫(Override using 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查詢。

更新(Update)

說明:更新操做,先獲取對象,進行修改操做以後,直接調用SubmitChanges()方法便可提交。注意,這裏是在同一個DataContext中,對於不一樣的DataContex看下面的講解。

1.簡單形式

Customer cust =
    db.Customers.First(c => c.CustomerID == "ALFKI");
cust.ContactTitle = "Vice President";
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將對檢索到的進行的更新保持回數據庫。

刪除(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更新(Update with Attach)

說明:在對於在不一樣的DataContext之間,使用Attach方法來更新數據。

例如在一個名爲tempdb的NorthwindDataContext中,查詢出Customer和Order,在另外一個NorthwindDataContext中,Customer的地址更新爲123 First Ave,Order的CustomerID 更新爲CHOPS。

//一般,經過從其餘層反序列化 XML 來獲取要附加的實體
//不支持將實體從一個DataContext附加到另外一個DataContext
//所以若要複製反序列化實體的操做,將在此處從新建立這些實體
Customer c1;
List<Order> deserializedOrders = new List<Order>();
Customer deserializedC1;
using (NorthwindDataContext tempdb = new NorthwindDataContext())
{
    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 = new NorthwindDataContext())
{
    //將第一個實體附加到當前數據上下文,以跟蹤更改
    //對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 = new NorthwindDataContext())
{
    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 = new NorthwindDataContext())
{
    //將第一個實體附加到當前數據上下文,以跟蹤更改
    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體驗(10)——LINQ to SQL語句之開放式併發控制和事務

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 = new NorthwindDataContext();
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 = new NorthwindDataContext() ;
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事務

LINQ to 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.SqlException e)
{
    //執行異常處理
}

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體驗(11)——LINQ to SQL語句之Null語義和String/DateTime方法

在本系列中,主要介紹LINQ to SQL基礎的東西,由於LINQ太強大了,它對咱們日常使用不一樣的數據源有着不一樣的內容,其包括對於SQL Server 數據庫的LINQ to SQL;對於XML 文檔的LINQ to XML;對於 ADO.NET 數據集的LINQ to DataSet;對於.NET 集合、文件、字符串等的LINQ to Objects。例外也出現了一些對LINQ支持的開源項目,例如LINQ to JSON,LINQ for NHibernate等等。在這個系列中,一些關於LINQ to SQL基礎的東西就這麼多了,這一篇用一些例子說明一下Null語義和String/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
    };

字符串/日期函數(String/Date Functions)

LINQ to SQL支持如下String方法。可是不一樣的是默認狀況下System.String 方法區分大小寫。而SQL則不區分大小寫。

1.字符串串聯(String Concatenation)

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支持如下DateTime方法。可是,SQL Server和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類型相比,前者範圍更大、精度更高。所以來自SQL Server的數據用CLR類型表示時,毫不會損失量值或精度。但若是反過來的話,則範圍可能會減少,精度可能會下降;SQL Server日期不存在TimeZone概念,而在CLR中支持這個功能。
咱們在LINQ to SQL查詢使用以當地時間、UTC 或固定時間要本身執行轉換。

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

16.DateTime.Year

var q =
    from o in db.Orders
    where o.OrderDate.Value.Year == 1997
    select o;

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

17.DateTime.Month

var q =
    from o in db.Orders
    where o.OrderDate.Value.Month == 12
    select o;

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

18.DateTime.Day

var q =
    from o in db.Orders
    where o.OrderDate.Value.Day == 31
    select o;

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

LINQ體驗(12)——LINQ to SQL語句之對象標識和對象加載

對象標識

  • 運行庫中的對象具備惟一標識。引用同一對象的兩個變量其實是引用此對象的同一實例。你更改一個變量後,能夠經過另外一個變量看到這些更改。
  • 關係數據庫表中的行不具備惟一標識。因爲每一行都具備惟一的主鍵,所以任何兩行都不會共用同一鍵值。

實際上,一般咱們是將數據從數據庫中提取出來放入另外一層中,應用程序在該層對數據進行處理。這就是 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;

對象加載

延遲加載

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

例如,您可能須要查看客戶數據和訂單數據。你最初不必定須要檢索與每一個客戶有關的全部訂單數據。其優勢是你可使用延遲加載將額外信息的檢索操做延遲到你確實須要檢索它們時再進行。請看下面的示例:檢索出來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 = new NorthwindDataContext();
DataLoadOptions ds = new DataLoadOptions();
ds.LoadWith<Customer>(p => p.Orders);//連接2個表,創建關聯
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 請求相關數據,以便稍後在檢索到的各個對象中導航時不須要對數據庫進行額外的往返。

延遲加載:AssociateWith方法

使用 AssociateWith 方法指定子查詢以限制檢索的數據量。
在下面的示例中,AssociateWith 方法將檢索的 Orders 限制爲當天還沒有裝運的那些 Orders。若是沒有此方法,則會檢索全部 Orders,即便只須要一個子集。可是生成SQL語句會發現生成了不少SQL語句。

NorthwindDataContext db2 = new NorthwindDataContext();
DataLoadOptions ds = new DataLoadOptions();
ds.AssociateWith<Customer>(
     p => p.Orders.Where(o => o.ShipVia > 1));
db2.LoadOptions = ds;
var custs =
     from c in db2.Customers
     where c.City == "London"
     select c;
foreach (var cust in custs)
{
    foreach (var ord in cust.Orders)
    {
        foreach (var orderDetail in ord.OrderDetails)
        {
           //能夠查詢出cust.CustomerID, ord.OrderID, ord.ShipVia,
           //orderDetail.ProductID, orderDetail.Product.ProductName
        }
    }
}

語句描述:原始查詢未請求數據,在所檢索到各個對象的連接中導航如何以觸發對數據庫的新查詢而了結。此示例還說明在延遲加載關係對象時可使用 Assoicate With 篩選它們。

預先加載:LoadWith方法和Associate With方法

這個例子說明:使用LoadWith方法來確保在檢索客戶信息的同時檢索訂單信息,在檢索訂單信息的同時檢索訂單詳細信息, 僅僅訪問一次數據庫。便可以在一個查詢中檢索許多對象。使用Associate With方法來限制訂單詳細信息的排序規則。

NorthwindDataContext db2 = new NorthwindDataContext();
DataLoadOptions ds = new DataLoadOptions();
ds.LoadWith<Customer>(p => p.Orders);
ds.LoadWith<Order>(p => p.OrderDetails);
ds.AssociateWith<Order>(
     p => p.OrderDetails.OrderBy(o => o.Quantity));
db2.LoadOptions = ds;
var custs = (
     from c in db2.Customers
     where c.City == "London"
     select c);
foreach (var cust in custs)
{
    foreach (var ord in cust.Orders)
    {
        foreach (var orderDetail in ord.OrderDetails)
        {
           //查詢cust.CustomerID, ord.OrderID
           //orderDetail.ProductID, orderDetail.Quantity
        }
    }
}

語句描述:在原始查詢過程當中使用 LoadWith 請求相關數據,以便稍後在檢索到的各個對象中導航時此示例還說明在急切加載關係對象時可使用 Assoicate With 對它們進行排序。

加載重寫

這個例子在Category類裏提供了一個LoadProducts分部方法。當產品的類別被加載的時候,就直接優先調用了LoadProducts方法來查詢沒有貨源的產品。

private IEnumerable<Product> LoadProducts(Category category)
{
    //在執行LINQ to SQL的時候,這個LoadProducts分部方法
    //優先加載執行,這裏用存儲過程也能夠. 
    return this.Products
        .Where(p => p.CategoryID == category.CategoryID)
        .Where(p => !p.Discontinued);
}

執行下面的查詢時,利用上面方法返回的數據進行下面的操做:

NorthwindDataContext db2 = new NorthwindDataContext();
DataLoadOptions ds = new DataLoadOptions();
ds.LoadWith<Category>(p => p.Products);
db2.LoadOptions = ds;
var q = (
     from c in db2.Categories
     where c.CategoryID < 3
     select c);
foreach (var cat in q)
{
    foreach (var prod in cat.Products)
    {
       //查詢cat.CategoryID, prod.ProductID
    }
}

語句描述:重寫 Category 類中的分部方法 LoadProducts。加載某種類別的產品時,調用 LoadProducts 以加載此類別中未停產的產品。

LINQ體驗(13)——LINQ to SQL語句之運算符轉換和ADO.NET與LINQ to SQL

運算符轉換

1.AsEnumerable:將類型轉換爲泛型 IEnumerable

使用 AsEnumerable<TSource> 可返回類型化爲泛型 IEnumerable 的參數。在此示例中,LINQ to SQL(使用默認泛型 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>。

ADO.NET與LINQ to SQL

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 = new TransactionScope())
{
    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 = new SqlConnection(connString);
nwindConn.Open();
//利用現有的ADO.NET鏈接來建立一個DataContext:
Northwind interop_db = new Northwind(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 對象作出的更改。

3.直接執行 SQL語句

1.直接執行SQL查詢

若是 LINQ to SQL 查詢不足以知足專門任務的須要,咱們可使用 ExecuteQuery 方法來執行 SQL 查詢,而後將查詢的結果直接轉換成對象。

var products = db.ExecuteQuery<Product>(
    "SELECT [Product List].ProductID,"+
    "[Product List].ProductName " +
    "FROM Products AS [Product List] " +
    "WHERE [Product List].Discontinued = 0 " +
    "ORDER BY [Product List].ProductName;"
);

語句描述:這個例子使用ExecuteQuery<T>執行任意 SQL 查詢,並將所得的行映射爲 Product 對象的序列。

2.直接執行SQL命令

採用DataContext 鏈接時,可使用ExecuteCommand來執行不返回對象的 SQL 命令。

db.ExecuteCommand
    ("UPDATE Products SET UnitPrice = UnitPrice + 1.00");

語句描述:使用ExecuteCommand執行任意SQL命令,本例中爲將全部產品單價提升 1.00 的批量更新。

LINQ體驗(14)——LINQ to SQL語句之存儲過程

存儲過程

在咱們編寫程序中,每每須要一些存儲過程,在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 Count By Region]")]
public int Customers_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 By City]")]
public ISingleResult<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 in result)
{
    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 Partial Customers Set]")]
public ISingleResult<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 Partial Customers Set]")]
[ResultType(typeof(WholeCustomersSetResult))]
[ResultType(typeof(PartialCustomersSetResult))]
public IMultipleResults Whole_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 class WholeCustomersSetResult
{
    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 class PartialCustomersSetResult
{
    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 compName in shape1)
{
    Console.WriteLine(compName.CompanyName);
}
//返回部分Customer結果集
result = db.Whole_Or_Partial_Customers_Set(2);
IEnumerable<PartialCustomersSetResult> shape2 =
 result.GetResult<PartialCustomersSetResult>();
foreach (PartialCustomersSetResult con in shape2)
{
    Console.WriteLine(con.ContactName);
}

語句描述:這個實例使用存儲過程返回「WA」地區中的一組客戶。返回的結果集形狀取決於傳入的參數。若是參數等於 1,則返回全部客戶屬性。若是參數等於 2,則返回ContactName屬性。

4.多個結果集

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

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

ALTER PROCEDURE [dbo].[Get Customer And Orders]
(@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 And Orders]")]
public ISingleResult<Get_Customer_And_OrdersResult>
Get_Customer_And_Orders([Parameter(Name="CustomerID",
DbType="NChar(5)")] string customerID)
{
     IExecuteResult result = this.ExecuteMethodCall(this,
     ((MethodInfo)(MethodInfo.GetCurrentMethod())), customerID);
     return ((ISingleResult<Get_Customer_And_OrdersResult>)
     (result.ReturnValue));
}

一樣,咱們要修改自動生成的代碼:

[Function(Name="dbo.[Get Customer And Orders]")]
[ResultType(typeof(CustomerResultSet))]
[ResultType(typeof(OrdersResultSet))]
public IMultipleResults Get_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 in customer)
{
    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, ref totalSales);
Console.WriteLine("Total Sales for Customer '{0}' = {1:C}", 
customerID, totalSales);

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

好了,就說到這裏了,其增刪改操做同理。相信你們經過這5個實例理解了存儲過程。

LINQ體驗(15)——LINQ to SQL語句之用戶定義函數

用戶定義函數

咱們能夠在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)]

public IQueryable<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 in db.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體驗(16)——LINQ to SQL語句之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(@"Data Source=.\SQLEXPRESS;

AttachDbFilename={0};Integrated Security=True;

Connect Timeout=30;User Instance=True;

Integrated Security = SSPI;", userMDF);

 NewCreateDB newDB = new NewCreateDB(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 = new NewCreateDB(userMDF);

 

if (newDB.DatabaseExists())

    Console.WriteLine("NewCreateDB數據庫存在");

else

    Console.WriteLine("NewCreateDB數據庫不存在");

數據庫更改

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

不管對象作了多少項更改,都只是在更改內存中的副本。並未對數據庫中的實際數據作任何更改。直到對DataContext顯式調用SubmitChanges,所作的更改纔會傳輸到服務器。調用時,DataContext會設法將咱們所作的更改轉換爲等效的SQL命令。咱們也可使用本身的自定義邏輯來重寫這些操做,但提交順序是由DataContext的一項稱做「更改處理器」的服務來協調的。事件的順序以下:

  1. 當調用SubmitChanges時,LINQ to SQL會檢查已知對象的集合以肯定新實例是否已附加到它們。若是已附加,這些新實例將添加到被跟蹤對象的集合。
  2. 全部具備掛起更改的對象將按照它們之間的依賴關係排序成一個對象序列。若是一個對象的更改依賴於其餘對象,則這個對象將排在其依賴項以後。
  3. 在即將傳輸任何實際更改時,LINQ to SQL會啓動一個事務來封裝由各條命令組成的系列。
  4. 對對象的更改會逐個轉換爲SQL命令,而後發送到服務器。

若是數據庫檢測到任何錯誤,都會形成提交進程中止並引起異常。將回滾對數據庫的全部更改,就像未進行過提交同樣。DataContext 仍具備全部更改的完整記錄。

下面代碼說明的是在數據庫中查詢CustomerID爲ALFKI的顧客,而後修改其公司名稱,第一次更新並調用SubmitChanges()方法,第二次更新了數據但並未調用SubmitChanges()方法。

//查詢

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

//更新數據並調用SubmitChanges()方法

cust.CompanyName = "YJingLee's Blog";

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。此方法對了解 LINQ to 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 = new StreamWriter(Server.MapPath("log.txt"), true);

db.Log = sw;

var q = from c in db.Customers

        where c.City == "London"

        select c;

sw.Close();

LINQ體驗(17)——LINQ to SQL語句之動態查詢

高級特性

本文介紹LINQ的高級特性,其包括你們都關心的動態查詢的用法,另外簡單提下ID標識這個知識。

動態查詢

有這樣一個場景:應用程序可能會提供一個用戶界面,用戶可使用該用戶界面指定一個或多個謂詞來篩選數據。這種狀況在編譯時不知道查詢的細節,動態查詢將十分有用。

在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]

ID標識

在前面這一點沒有說到,在這裏做爲高級特性單獨說下ID標識。

這個例子說明咱們存儲一條新的記錄時候,ContactID做爲主鍵標識,系統自動分配,標識種子爲1,因此每次自動加一。

//ContactID是主鍵ID,插入一條數據,系統自動分配ID

Contact con = new Contact()

{

    CompanyName = "New Era",

    Phone = "(123)-456-7890"

};

db.Contacts.InsertOnSubmit(con);

db.SubmitChanges();

LINQ體驗(18)——LINQ to SQL語句之視圖和繼承支持

視圖

咱們使用視圖和使用數據表相似,只需將視圖從「服務器資源管理器/數據庫資源管理器」拖動到O/R 設計器上,自動能夠建立基於這些視圖的實體類。咱們能夠同操做數據表同樣來操做視圖了。這裏注意:O/R 設計器是一個簡單的對象關係映射器,由於它僅支持 1:1 映射關係。換句話說,實體類與數據庫表或視圖之間只能具備 1:1 映射關係。不支持複雜映射(例如,將一個實體類映射到多個表)。可是,能夠將一個實體類映射到一個聯接多個相關表的視圖。 下面使用NORTHWND數據庫中自帶的Invoices、Quarterly Orders兩個視圖爲例,寫出兩個範例。

查詢:匿名類型形式

咱們使用下面代碼來查詢出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].[Quarterly Orders] AS [t0]

繼承支持

LINQ to SQL 支持單表映射,其整個繼承層次結構存儲在單個數據庫表中。該表包含整個層次結構的全部可能數據列的平展聯合。(聯合是將兩個表組合成一個表的結果,組合後的表包含任一原始表中存在的行。)每行中不適用於該行所表示的實例類型的列爲 null。
單表映射策略是最簡單的繼承表示形式,爲許多不一樣類別的查詢提供了良好的性能特徵,若是咱們要在 LINQ to SQL 中實現這種映射,必須在繼承層次結構的根類中指定屬性 (Attribute) 和屬性 (Attribute) 的屬性 (Property)。咱們還可使用O/R設計器來映射繼承層次結構,它自動生成了代碼。

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

 

咱們學習的時候仍是看看其生成的代碼吧!

具體設置映射繼承層次結構有以下幾步:

  1. 根類添加TableAttribute屬性。
  2. 爲層次結構中的每一個類添加InheritanceMappingAttribute屬性,一樣是添加到根類中。每一個 InheritanceMappingAttribute屬性,定義一個Code屬性和一個Type屬性。Code屬性的值顯示在數據庫表的IsDiscriminator列中,用來指示該行數據所屬的類或子類。Type屬性值指定鍵值所表示的類或子類。
  3. 僅在其中一個InheritanceMappingAttribute屬性上,添加一個IsDefault屬性用來在數據庫表中的鑑別器值在繼承映射中不與任何Code值匹配時指定回退映射。
  4. 爲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 in db.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  [Northwind Shipper]

-- @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  [Northwind Shipper]

-- @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]

相關文章
相關標籤/搜索