對象之間的關系
既然是對象-關系映射,各個表之間肯定不是獨立存 在的(如果都是獨立存在的,也沒有必要用關系數據庫了),那麼就必然涉及到幾 個表之間的聯合了。
Linq to SQL和SQL語句一樣,支持兩種方式的聯合 :
1.利用where子句,對兩個表進行查找
2.使用join子句
我們還是用例子來說明吧,現在要對blogs和posts進行查詢,傳入一篇文章的id 的時候,找出這篇文章相關信息的同時還要找出這篇文章屬於哪個博客,接著上 篇的例子,我們首先得給Blog類加上映射:
博客類映射(Blog)
/**////
/// 博客類
///
[Table (Name="blogs")]
public class Blog
{
/**////
/// 博客標識
///
[Column (Name="blogid",IsPrimaryKey=true,IsDbGenerated=true)]
public int Id { get; set; }
/**////
/// 用戶標識,和用戶相關聯
///
[Column]
public int UserId { get; set; }
/**////
/// 博客的中文名
///
[Column]
public string Name { get; set; }
/**////
/// 創建時間
///
[Column]
public DateTime CreateDate { get; set; }
/**////
/// 一個博客有零篇或多篇文章,這個就先不管了
/// 不 建立映射關系
///
public IList<Post> Posts { get; set; }
}
首先獲取Post和Blog的Table的對 象,然後施加操作:
Table<Blog> blogs = dbContext.GetTable<Blog>();
Table<Post> posts = dbContext.GetTable<Post>();
var result = from blog in blogs
from post in posts
where post.BlogId == blog.Id && post.Id == 2
select new {
BlogName = blog.Name,
PostTitle = post.Title,
PostBody = post.Body
};
foreach (var item in result)
Console.WriteLine(item.BlogName);
下面是Linq to SQL為我們生成的SQL語句,
從 上面可以看出來是用where做聯結的條件來進行的,這種聯結的方式是不被推薦 的,存在於ANSI-82 SQL的標准中推薦 的方式是使用join子句:
Table<Blog> blogs = dbContext.GetTable<Blog>();
Table<Post> posts = dbContext.GetTable<Post>();
var result = from blog in blogs
join post in posts on blog.Id equals post.BlogId
where post.Id == 2
select new {
BlogName = blog.Name,
PostTitle = post.Title,
PostBody = post.Body
};
foreach (var item in result)
Console.WriteLine(item.BlogName);
生成的SQL語句是:
大家 看到,Linq to SQL使用inner join子句。
但是Linq to SQL在使用join的時 候並不是像SQL那樣寬松,把上面的SQL語句貼下來:
SELECT [t0].[blogname] AS [BlogName],[t1].[Title] AS [PostTitle],[t1].[Body] AS [PostBody] FROM [blogs] AS [t0] INNER JOIN [posts] AS [t1] ON [t0].[blogid] = [t1].[BlogId] WHERE [t1].[postid] = @p0
在
SQL語句裡,ON子句的 [t0].[blogid] = [t1].[BlogId]左右次序是沒有關系的:[t0].[blogid] = [t1].[BlogId]或[t1].[BlogId] = [t0].[blogid]的結果是一樣的,但是在Linq to SQL裡並不是如此,她要求查詢的對象和ON子句裡面的東西的位置是一一對應 的:
如上圖,用框子框起來的四部分,橙黃色的兩個,藍色的兩個,位置都是一 一對應的,blog排在第一位,那麼blog.Id就要排在on的第三位,1,3和2,4這種 對應關系。這又是為什麼呢?因為這種查詢表達式最後要被轉換成方法調用,而 擴展方法Join的原型是:
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>
(
this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector
)
大家看到沒,第一個參數和第三個參數都是outer相關的,第二個參數和第四個 參數都是inner相關的,最後一個是on條件時的條件運算符,如:equals。將上 面的方法原型和上面那張圖對應:blogs就是這裡的outer,而blog.Id對應著這 裡的outerKeySelector,posts就是這裡的inner,post.BlogId對應著這裡的 innerKeySelector,equals就是這裡的resultSelector。實際上,如果你使用 VisualStudio2008編寫代碼的時候,如果不按照這個順序敲入代碼,vs是不會給 你智能感知的。
大家也許發現上面的join生成的SQL語句只有inner join(inner join也稱之為等值連接,只返回兩個表聯結字段相等的行),在我們 這個需求裡,這樣使用是合理的:查找一篇文章,如果該文章對應的博客不存在 的話,這篇文章也不應該顯示。但是,如果我們期望使用outer join咋辦呢? Linq為我們提供了一個DefaultIfEmpty()的擴展方法:
var result = from post in posts
join blog in blogs on post.BlogId equals blog.Id into joinblogs
from joinblog in joinblogs.DefaultIfEmpty()
where post.Id == 2
select new {
BlogName = joinblog.Name,
PostTitle = post.Title,
PostBody = post.Body
};
這樣生成 的SQL語句就是這樣的了:
看到 上面說了那麼多,也許有人會問:一般的ORM框架都能在對象之間建立關系來反 映數據庫之間的關系,而操作的時候這種關聯是自動的,難道Linq to SQL必須 讓程序員手動的使用這種join的方式來處理對象之間的關系麼?答案是否定的。
一對多關系
在上面的映射中,Blog還有一個屬性:Posts,我們並沒 有做什麼處理,實際上這個應該是根據Blog的Id屬性關聯到Post的BlogId屬性, 進行關聯的。
為此我們要修改一下這個Blog的映射:
///
/// 一個博客有零篇或多篇文章,
///
[Association(OtherKey = "BlogId")]
public EntitySet<Post> Posts { get; set; }
使用Association 建立映射關系,她有一個OtherKey屬性,該屬性指定為被關聯的那個對象中做連 接的那個屬性(外鍵)(這裡被關聯的就是Post類,做連接的那個屬性就是BlogId ,她也是posts表的外鍵),還要注意的是,我們要把原來使用的IList換成 EntitySet,EntitySet是Linq新提供的一個類,在System.Data.Linq命名空間下 。
下面來看看例子:
var result = from blog in dbContext.GetTable<Blog>()
where blog.Id == 1
select blog;
foreach (var item in result)
{
Console.WriteLine(item.Name);
if (item.Posts == null || item.Posts.Count <= 0) return;
foreach (var post in item.Posts)
{
Console.WriteLine(post.Title);
}
}
我們只要對blog進行查詢就ok了,和她關聯的 Post自動的會映射過來的。
上面的例子只表明了在Blog類裡,如何關聯 到該博客的所有Post,如果我在Post裡想關聯到該Post屬於哪個博客呢?
在Post類裡添加:
//注意,用的是EntityRef
這樣就ok了,你查一個Post的時候會自動的把其所屬的博客也給 弄出來哦。而且這個EntityRef還是支持延遲加載的。
private EntityRef<Blog> _blog;
//從這裡可以看出 Storage屬性的作用了吧,這裡的ThisKey指定的是
//本類裡面的 BlogId,一個外鍵,並不是主鍵了哦。
[Association (Storage="_blog",ThisKey="BlogId")]
public Blog Blog
{
get { return _blog.Entity; }
set { _blog.Entity = value; }
}
多對多的關系
Linq to SQL默認是不支持多對多的關系的,所以也沒有針對多對多關系 的Attribute和Query,不過網上有很多多對多關系的解決方案,在這裡我就不做 介紹了,我給個連接:
http://blogs.msdn.com/mitsu/archive/2007/06/21/how-to-implement-a- many-to-many-relationship-using-linq-to-sql.aspx
這位大哥的方案 是一個通用的,你可以下載他的代碼應用到你的項目當中,有時間我會把這個翻 譯一下。
一對一的關系(one-to-one)
一對一的關系Linq to SQL 是支持的,你只要給關系的兩邊都加上EntitySet屬性就可以了,下面用實例做 個演示:
在我們的實例中,每個用戶對應著一個博客,每個博客也只對 應著一個用戶,這是個一對一的關系:
用戶類映射(User)
/**////
/// 用戶類
///
[Table (Name="users")]
public class User
{
/**////
/// 用戶標識
///
[Column (Name="userid",IsPrimaryKey=true,IsDbGenerated=true)]
public int Id { get; set; }
[Column]
public int BlogId { get; set; }
/**////
/// 該用戶對應的博客,
/// 一個用戶有且僅有一個博客
///
[Association (ThisKey="BlogId",OtherKey="Id",IsUnique=true)]
public EntitySet<Blog> Blog { get; set; }
/**////
/// 用戶名
///
[Column]
public string UserName { get; set; }
/**////
/// 密碼
///
[Column]
public string Password { get; set; }
/**////
/// 昵稱
///
[Column]
public string NickName { get; set; }
/**////
/// 用戶離開時間,臨時存儲用戶離開時間的,數據 庫
/// 裡並沒有對應的字段,所以不做映射
///
public DateTime LeaveTime { get; set; }
}
Blog類前面已經出現過,我們就不列出全部代碼,將Blog類修改如下:
[Column]
public int UserId { get; set; }
[Association (ThisKey="UserId",OtherKey="Id",IsUnique=true)]
public EntitySet<User> User { get; set; }
就是添 加一個EntitySet User的屬性而已。
看看操作:
通過查詢結果可以看出,他們之間的關系建立了。
後記
本來這一 篇我准備了好幾個內容,但是在上篇評論裡,有人說我的一篇太長了,確實,如 果太長了,沒有多少人能有耐心看下去,而且看的時間太長對眼睛也不好,在辦 公室裡也不好意思老頂著屏幕看博客吧,boss看了也不好啊,所以這篇就光說一 個關聯了,那看來這個How do I原計劃的三篇是不能完成了。其實文章有點長, 看起來應該很快,沒有什麼難度的內容,而且我是以講話的風格寫的,本系列的 內容我的計劃是把Linq講的透透徹徹的,從表面上如何使用,到後面是怎麼實現 的都說一遍,所以每一篇都是整個系列的一個元組。
看看操作:
通過查詢結果可以看出,他們之間的關系建立了。
後記
本來這一 篇我准備了好幾個內容,但是在上篇評論裡,有人說我的一篇太長了,確實,如 果太長了,沒有多少人能有耐心看下去,而且看的時間太長對眼睛也不好,在辦 公室裡也不好意思老頂著屏幕看博客吧,boss看了也不好啊,所以這篇就光說一 個關聯了,那看來這個How do I原計劃的三篇是不能完成了。其實文章有點長, 看起來應該很快,沒有什麼難度的內容,而且我是以講話的風格寫的,本系列的 內容我的計劃是把Linq講的透透徹徹的,從表面上如何使用,到後面是怎麼實現 的都說一遍,所以每一篇都是整個系列的一個元組。
看看操作:
通過查詢結果可以看出,他們之間的關系建立了。
後記
本來這一 篇我准備了好幾個內容,但是在上篇評論裡,有人說我的一篇太長了,確實,如 果太長了,沒有多少人能有耐心看下去,而且看的時間太長對眼睛也不好,在辦 公室裡也不好意思老頂著屏幕看博客吧,boss看了也不好啊,所以這篇就光說一 個關聯了,那看來這個How do I原計劃的三篇是不能完成了。其實文章有點長, 看起來應該很快,沒有什麼難度的內容,而且我是以講話的風格寫的,本系列的 內容我的計劃是把Linq講的透透徹徹的,從表面上如何使用,到後面是怎麼實現 的都說一遍,所以每一篇都是整個系列的一個元組。