程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 走進Linq-Linq to SQL How do I(2)

走進Linq-Linq to SQL How do I(2)

編輯:關於.NET

對象之間的關系

既然是對象-關系映射,各個表之間肯定不是獨立存 在的(如果都是獨立存在的,也沒有必要用關系數據庫了),那麼就必然涉及到幾 個表之間的聯合了。

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
     private EntityRef<Blog> _blog;
    //從這裡可以看出 Storage屬性的作用了吧,這裡的ThisKey指定的是
    //本類裡面的 BlogId,一個外鍵,並不是主鍵了哦。
    [Association (Storage="_blog",ThisKey="BlogId")]
     public Blog Blog
    {
      get { return _blog.Entity; }
      set { _blog.Entity = value; }
     }
這樣就ok了,你查一個Post的時候會自動的把其所屬的博客也給 弄出來哦。而且這個EntityRef還是支持延遲加載的。

多對多的關系

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講的透透徹徹的,從表面上如何使用,到後面是怎麼實現 的都說一遍,所以每一篇都是整個系列的一個元組。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved