程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Linq To Sql進階系列(二)M:M關系

Linq To Sql進階系列(二)M:M關系

編輯:關於.NET

在Linq To Sql進階系列(一) 一文中,我們談到了數據庫中的兩種基本關系1:M 與1:1. 而現實世 界中,還有一種M:M 的關系。比如,一個老師可以有多個學生,而一個學生也可以有多個老師。老師和 學生的關系就是多對多的關系。這些關系在數據庫中是如何反映的呢?

在C#3.0入門系列(十)-之Join操作一文中,我們提到了M:M 的關系中的join操作。哦,原來,M:M 的關系在數據庫中,依然是通過1:M 來體現。比如,在一個域內,一個User可以加入到多個Group中,一 個Group也可以包含多個User。 User與Group並沒有直接的關系,而是通過第三個表UserInGroup發生關 系。User與 UserInGroup的關系為1:M,其關系鍵為UserId, 而Group與 UserInGroup的關系也為1:M, 其 關系鍵為GroupId,這樣,我們通過第三個表,讓User與Group發生了關系,他們的關系為M:M.

這三個表的腳本如下:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[User]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
CREATE TABLE [dbo].[User](
[UserId] [nchar](10) NOT NULL,
[UserName] [nchar](10) NULL,
  CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED
(
[UserId] ASC
) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[Group]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
CREATE TABLE [dbo].[Group](
[GroupId] [nchar](10) NOT NULL,
[GroupName] [nchar](10) NULL,
  CONSTRAINT [PK_Group] PRIMARY KEY CLUSTERED
(
[GroupId] ASC
) ON [PRIMARY]
) ON [PRIMARY]
END
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo].[UserInGroup]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1)
BEGIN
CREATE TABLE [dbo].[UserInGroup](
[UserId] [nchar](10) NOT NULL,
[GroupId] [nchar](10) NOT NULL,
  CONSTRAINT [PK_UserInGroup] PRIMARY KEY CLUSTERED
(
[UserId] ASC,
[GroupId] ASC
) ON [PRIMARY]
) ON [PRIMARY]
END
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo]. [FK_UserInGroup_Group]') AND type = 'F')
ALTER TABLE [dbo].[UserInGroup]  WITH CHECK ADD  CONSTRAINT [FK_UserInGroup_Group] FOREIGN KEY([GroupId])
REFERENCES [dbo].[Group] ([GroupId])
GO
ALTER TABLE [dbo].[UserInGroup] CHECK CONSTRAINT [FK_UserInGroup_Group]
GO
IF NOT EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[dbo]. [FK_UserInGroup_User]') AND type = 'F')
ALTER TABLE [dbo].[UserInGroup]  WITH CHECK ADD  CONSTRAINT [FK_UserInGroup_User] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([UserId])
GO
ALTER TABLE [dbo].[UserInGroup] CHECK CONSTRAINT [FK_UserInGroup_User]

他們的關系圖如下:

創建數據庫後,添加一些數據供測試。

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將他們映射為實體。關於OR Designer請參考C#3.0入門系列(七)--之OR工具介紹 (上)/C#3.0入門系列(七)--之OR工具介紹(下)

現在的問題是:User和Group的實體對象中,可不可以直接引用對方呢?就像User.Groups 或 Group.Users。我們可不可以去改變其映射,直接越過UserInGroup,讓User和Group直接發生關系呢?回 答是肯定的。但是,數據庫中,依然是三個表。你不可能在數據庫中越過表UserInGroup的。表 UserInGroup起了一個紐帶的作用,想把它拋棄掉,不太可能。那可不可以,在mapping code中,做些手 腳,讓Linq To Sql自己去找這個紐帶呢?回答是,Linq To Object可以,但是, Linq To Sql不可以。

為什麼Linq To Sql不可以?其原因就是Linq To Sql需要先翻譯成Sql語句,而,這個映射,必須真 實反映數據庫裡表的情況。也就說,這個映射,無法逾越UserInGroup實體。它必須要有UserInGroup。

那Linq To Object怎麼樣可以呢?把下面的代碼,加入到你的工程中。

using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.ComponentModel;
using System;

namespace ConsoleApplication1
{
    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();
            }
        }
    }
}

為了不破壞數據庫的映射,我們把這部分改動放到partial  class中。這樣,我們渴望實現 user.Group。

我們來做個測試,看看是不是成功了呢。

public static void Main(string[] arg)
    {
        DataClasses1DataContext db = new DataClasses1DataContext();
        db.Log = Console.Out;
        var userSet = (from u in db.Users
                       select u).ToList();
        foreach (var use in userSet)
        {
            foreach (var group in use.Groups)
            {
                Console.WriteLine(group.GroupName);
            }
        }

        var groupSet = (from u in db.Groups
                        select u).ToList();
        foreach (var group in groupSet)
        {
            foreach (var user in group.Users)
            {
                Console.WriteLine(user.UserName);
            }
        }
    }

太好了。這正是我們想要的。那再來做個反面的測試吧。

var q = (from u in db.Users
                  from g in u.Groups
                  select new { u.UserName, g.GroupName }).ToList ();

編譯通過,在run-time時,出錯。因為,Linq To Sql不知道該怎麼去找這個關系。那麼下面這個呢 ?

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 ();

run-time沒有問題。糾起原因,其在裡面已經取回了數據,是Linq To Object的范疇了。

還可以使用下面的這個映射code。

using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Serialization;
using System.ComponentModel;
using System;

namespace ConsoleApplication1
{
    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;
            }
        }
    }
}

結合我們的測試例子,請大家仔細比較他們的區別哦。前面那個是一次性取出了。後面這個是用那個 取那個。

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