首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

LINQ to SQL 進階系列(二):多對多關聯實作

     編輯:關於.NET

現實世界常見多對多(M:M)關聯,如「老師-學生」、「使用者-群組」。資料庫中無法直接表達 M:M,必須透過中介表(Junction Table)轉換為兩組一對多(1:M)關聯。

資料庫結構範例

以 User、Group、UserInGroup 三表為例:

-- User 表
CREATE TABLE [dbo].[User] (
    [UserId] [nchar](10) NOT NULL PRIMARY KEY,
    [UserName] [nchar](10) NULL
);

-- Group 表
CREATE TABLE [dbo].[Group] (
    [GroupId] [nchar](10) NOT NULL PRIMARY KEY,
    [GroupName] [nchar](10) NULL
);

-- 中介表 UserInGroup
CREATE TABLE [dbo].[UserInGroup] (
    [UserId] [nchar](10) NOT NULL,
    [GroupId] [nchar](10) NOT NULL,
    PRIMARY KEY ([UserId], [GroupId]),
    FOREIGN KEY ([UserId]) REFERENCES [dbo].[User]([UserId]),
    FOREIGN KEY ([GroupId]) REFERENCES [dbo].[Group]([GroupId])
);

ER 圖:User - UserInGroup - Group

測試資料

INSERT INTO [User] VALUES ('Tom Song', 'Tom Song');
INSERT INTO [User] VALUES ('Guoan Song', 'Guoan Song');
INSERT INTO [Group] VALUES ('Csharp', 'Csharp Team');
INSERT INTO [Group] VALUES ('Dlinq', 'Dlinq Team');
INSERT INTO UserInGroup VALUES ('Tom Song', 'Csharp');
INSERT INTO UserInGroup VALUES ('Tom Song', 'Dlinq');
INSERT INTO UserInGroup VALUES ('Guoan Song', 'Csharp');
INSERT INTO UserInGroup VALUES ('Guoan Song', 'Dlinq');

實體類別與導覽屬性

使用 OR Designer 產生實體後,User 與 Group 之間無直接導覽屬性,必須透過 UserInGroup 實體間接存取。若想在程式碼中直接使用 user.Groups 或 group.Users,可利用 partial class 擴充:

public partial class Group {
    public List<User> Users {
        get {
            return (from u in this.UserInGroups select u.User).ToList();
        }
    }
}

public partial class User {
    public List<Group> Groups {
        get {
            return (from g in this.UserInGroups select g.Group).ToList();
        }
    }
}

LINQ to SQL vs LINQ to Objects 的差異

LINQ to Objects(記憶體查詢) 可直接使用上述擴充屬性:

var userSet = db.Users.ToList(); // 先取回記憶體
foreach (var u in userSet)
    foreach (var g in u.Groups) // OK:在記憶體中運算
        Console.WriteLine(g.GroupName);

LINQ to SQL(轉譯為 SQL) 則會失敗,因為無法將擴充屬性轉譯為有效的 SQL JOIN:

// 編譯通過,執行時期拋出例外
var q = (from u in db.Users
         from g in u.Groups // 無法轉譯為 SQL
         select new { u.UserName, g.GroupName }).ToList();

解決方案:先 .ToList() 將資料載入記憶體,再進行後續查詢(轉為 LINQ to Objects):

var q2 = (from u1 in (from u in db.Users select u).ToList()
          from g in u1.Groups
          select new { u1.UserName, g.GroupName }).ToList(); // 正常執行

改用 IEnumerable<T> 延遲執行

將擴充屬性型別改為 IEnumerable<T> 並移除 .ToList(),可獲得延遲執行特性(逐筆產生),記憶體效率較佳:

public partial class Group {
    public IEnumerable<User> Users {
        get { return from u in this.UserInGroups select u.User; }
    }
}

public partial class User {
    public IEnumerable<Group> Groups {
        get { return from g in this.UserInGroups select g.Group; }
    }
}

差異總結: List<T> 版本會一次性載入所有關聯資料;IEnumerable<T> 版本則按需逐筆產生,適合大量資料場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved