本文詳細為你闡述了如何在你的應用程序中實現LINQ to SQL。附件的示例程序包括了這里探討的所有代碼,還提供了一個簡單的WPF圖形界面程序來顯示通過數據綁定返回的結果集。
本部分描述如何實現表間的映射關系:M:1,1:M和M:M。但是這里不會討論1:1的映射關系,你可以在M:1的關系中發現這種1:1的映射關系。因此,從這里開始,我們將使用Book作為示例為你一步一步講述這一實現過程。
映射M:1的關系
Book 對象與Category 對象是多對一的關系(M:1),因為一本書僅能屬于某一個類別(并且每個類別能夠包涵很多本書):

在數據庫中,Book.catalog字段作為該表的外鍵,而在Category中作為主鍵。然而,在你的對象模型中,你很可能想讓book.Catalog表示一個實際的Catalog對象(不僅僅是ID)。此時,你可以通過創建兩個私有字段來實現這一映射關系,然后再對Category對象暴露一個公有屬性。
1、添加一個私有字段以進行其他表的關聯
添加一個私有字段,將其映射到Book.category數據庫表的外鍵列。
如果允許該字段為NULL,使用一個空類型即可實現(如,采用Int?的方式)。
我將這個字段命名為categoryId(為了區別于我們后面將要創建的公有屬性Category)。這意味著在Column特性上我必須得設置Name參數,因為我的字段名字和數據庫表的字段名稱不同:
[Column( Name="Category")]privateint?categoryId; |
2、添加一個引用其他表的私有EntityRef類型字段
添加一個私有類型的EntityRef字段以實現對Category實例的引用。雖然這會使得公有屬性Category扮演一個后臺字段的角色,但是通過使用EntityRef類型,仍然能達到延遲加載的目的(意思是LINQ不會即時從數據庫獲取數據直到我們真正想要數據的時候)。初始化EntityRef字段實例,以阻止NullReferenceExceptions發生。萬一在某個地方你需要使用該對象的實例(如,Category)而此時又沒有任何實例(Book)可用。
privateEntityRef _category=newEntityRef( ); |
3、使用[Association]特性添加一個公有屬性進行類的關聯
最后,創建類型為public的Category屬性,該屬性用于接收實際的Category實例。 使用下面的參數為該屬性添加Association特性:
● 數據庫兩表之間的是關系名稱Name(在本例為FK_Books_BookCategories))
● IsForeignKey = true標記指定該類對應數據表的外鍵(在下面的ThisKey參數中進行了指定)。
● 使用剛才創建的字段為該特性設置兩個參數:
(1)ThisKey用于指定到其它表的外鍵:categoryId。
(2)Storage指定EntityRef用于接收其他類的實例:_category。
在屬性內部,代碼的getter/setter訪問器用于使用category的Entity屬性,它包涵實際的Category實例:
[Association( Name = "FK_Books_BookCategories", IsForeignKey = true, Storage = "_category", ThisKey = "categoryId" )] public Category Category{ get { return _category.Entity; } set { _category.Entity = value; } } |
從M:1的關系上訪問數據
現在你可以通過這種關系以面向對象的方式訪問Category的數據。例如,book.Category.Name:
foreach( var book in bookCatalog.Books ) { string categoryName = book.Category.Name; } |
在關聯表中的M:1關系
現在,咱們一起看看如何在M:M關聯表中實現M:1的關系。當我們遇到表之間的關系為M:M時,正如這里的BookAuthors,當然,連接表的關系仍然是由兩個M:1關系構成。
例如,BookAuthor包涵一個到Book的M:1關系和一個到Author的M:1關系來構成M:M的關系:

不幸的是,你仍然需要創建一個類來映射這個關聯表。然而,由于外鍵的原因,它僅僅用于映射的關系,你可以通過設置類的訪問修飾符為internal來保持僅對外提供一個公共接口。
1、使用[Table]特性創建一個內部類用于映射需要連接的表
以BookAuthors類相同的方式創建為其他實體類,但是不要將其標記為public:
using System.Data.Linq; using System.Data.Linq.Mapping; namespace LINQDemo { [Table( Name = "BookAuthors" )] class BookAuthor{} } |
2、映射兩表之間的M:1關系,指定它們將其作為主鍵
為Book和Author創建一個M:1的關系并以相同的方式為Book:Catalog創建關系。注意數據庫中BookAuthors表的關系以下面的方式命名:
● BookAuthor:Authors關系被命名為 FK_BookAuthors_Authors
● BookAuthor:Books 關系被命名為FK_BookAuthors_Books
分別在它的兩個Column特性上為其添加IsPrimaryKey = true的屬性以指示BookAuthors的主鍵由這兩個值構成:
[Table( Name = "BookAuthors" )] class BookAuthor { [Column( IsPrimaryKey = true, Name = "Author" )] private int authorId; private EntityRef _author = new EntityRef( ); [Association( Name = "FK_BookAuthors_Authors", IsForeignKey = true, Storage = "_author", ThisKey = "authorId" )] public Author Author { get { return _author.Entity; } set { _author.Entity = value; } } Column( IsPrimaryKey = true, Name = "Book" )] private int bookId; private EntityRef _book = new EntityRef( ); [Association( Name = "FK_BookAuthors_Books", IsForeignKey = true, Storage = "_book", ThisKey = "bookId" )] public Book Book { get { return _book.Entity; } set { _book.Entity = value; } } } |
雖然我們已經探討了M:1的關系,但不幸的是,你仍然還不能使用BookAuthor做一些有趣的事,當我們在下面討論M:M的關系時,將繼續回到表連接的探討話題上來。但首先,通過理解如何映射1:M的關系,我們可以看看Book:Catalog的關系來完整理解M:M的關系。
映射1:M關系
添加一個1:M的關系使得你可以獲得一個屬于Category的所有書籍列表。
1、在其它類中映射外鍵
即使你正在為Category添加關聯關系,它仍然需要知道如何關聯到Book本身。因此,你僅需要確保你的Book類已經映射到了對應的列,該列應該是關聯到Category的外鍵。如果你這樣實現,那么你已經完成了1:M的關系,因此這時你所要做的就是指定字段名:categoryId:
[Table( Name = "Books" )] public class Book { ... [Column( Name = "Category" )] private int? categoryId; |
2、映射你自己的主鍵
LINQ會比較Book的外鍵和Category的外鍵,因此你需要映射Category.Id并標識其作為主鍵([Column (IsPrimaryKey = true)])。再次,如果你繼續這個步驟,你已經在實體類中完成了該過程的創建。因此,所要做的僅僅是為該屬性名Id添加一個注釋:
[Table (Name="BookCategories")] public class Category { [Column ( IsPrimaryKey = true, IsDbGenerated = true )] public int Id { get; set; } ... |
3、添加一個私有的EntitySet類型以實現對其他表的引用
添加一個私有的EntitySet字段來接收屬于該Category的書籍。這將讓我們的公有Books屬性扮演字段的角色。類似地,EntityRef,EntitySet會引起對Books的加載延遲直到我們實際訪問它的時候(因此,當我們需要查看一個類別的時候,我們不必每次都返回所有書的列表)。
初始化EntitySet字段以避免NullReferenceExceptions異常的發生,比如在你沒有設置兩邊關系的時候(如一個類別沒有書籍的時候)
private EntitySet _books = new EntitySet(); |
4、為相關聯的類添加屬性
最后創建公有Books屬性,它用于接收在對應類別中的書籍。為該屬性添加一個Association特性。并設置數據庫關系的名稱參數為Name (FK_Books_BookCategories),以及使用剛才創建這個字段的特性所需要的三個參數:
● OtherKey指定關聯到其他類(Book)的字段,該字段接收所關聯的外鍵:categoryId
● ThisKey指定你的主鍵(OtherKey所應該匹配的字段):Id
● Storage指定你的EntitySet類型,該類型用來存儲相關聯的Books集合:_books.
在屬性內部,代碼getter/setter訪問修飾符使用了 _books,它包涵一個實際Book實例的ICollection集合:
[Association( Name = "FK_Books_BookCategories", Storage = "_books", OtherKey = "categoryId", ThisKey = "Id" )] public ICollection Books { get { return _books; } set { _books.Assign( value ); } } |
從1:M 關系上訪問數據
現在,你可以通過使用category.Books屬性訪問每個類別中的Books列表:
foreach( var category in bookCatalog.Categories ){ foreach( Book book in category.Books ){ string bookTitle = book.Title; } } |
映射M:M 關系
最后,添加M:M關系,這可以允許你直接從Book實例中訪問每個目錄下下的所有作者,以及從Author實例中直接訪問某個作者寫的所有書籍。
通過創建BookAuthor類,說明你已經完成了該任務的大部分工作了。并且,你也在前面部分已經了解到當你需要創建1:M時如何實現這一功能。現在,是該將它們整合起來的時候了。再次,我們將使用Book作為示例進行闡述。
1、從類到關聯的表上添加一個1:M的關系
Book到BookAuthor 它存在一個1:M的關系,每本書都可以有多個作者,但是每本書作者僅僅屬于一本書。因此,你必須得從先前已有的部分開始進行如下四個步驟:
● bookId. 在BookAuthor類中映射到Book的外鍵。你已經這樣實現了,會調用它的:bookId。
● 為Book對象定義主鍵。你也已經這樣實現了,會調用它的:Id。
● Add an EntitySet that references the other table: _bookAuthors. 添加一個引用其他表的EntitySet類型。
● 添加一個BookAuthors屬性(將其設置為私有的,因為這里僅僅用于幫助獲得作者列表),該屬性添加了Association特性并為其設置前面部分設置的三個參數值。
添加Book對象代碼的第3和第4步如下:
[Table( Name = "Books" )] public class Book { ... private EntitySet _bookAuthors = new EntitySet( ); [Association( Name = "FK_BookAuthors_Books", Storage = "_bookAuthors", OtherKey = "bookId", ThisKey = "Id" )] private ICollection BookAuthors { get { return _bookAuthors; } set { _bookAuthors.Assign( value ); } } |
雖然你可以在Author中實現相同的功能(因為每個作者都可以有多本書),但是使用authorId取代bookId:
[Table( Name = "Authors" )] public class Author { ... private EntitySet _bookAuthors = new EntitySet( ); [Association( Name = "FK_BookAuthors_Authors", Storage = "_bookAuthors", OtherKey = "authorId", ThisKey = "Id" )] private ICollection BookAuthors { get { return _bookAuthors; } set { _bookAuthors.Assign( value ); } } |
2、添加一個公有屬性,通過1:M的關系使用LINQ 來檢索枚舉數據。
最后,通過創建Authors屬性從該書實例的私有書作者列表中檢索所有作者。例如,如果你有一本LINQ In Action的書,該書就有三個作者:Fabrice Marguerie, Steve Eichert, 和Jim Wooley,那么LINQ In Action Book實例將包涵三個BookAuthors的列表:
