twMVC



 

 

 

技術資訊

線上書店

最新回應

昨天才寫完 Entity Framework 2.0 處理外來鍵的功能,馬上就有人問說能不能使用在多對多關聯上,顧名思義,多對多關聯 (Many-to-Many Relationship) 是用在兩個表格之間彼此的屬性都可以做一對多關聯的情況,最常看到的例子是產品線 (Product Lines),活動行銷與產品組合 (Product Collections for Marketing) 或是學生的選課系統 (Course Registrations),在資料庫設計上使用非常廣泛,在規劃資料庫設計 ERD 時,這種關聯性可以以圖形或 UML 來表示,但到了正規化時,必須要切割成獨立的一個表格來串接兩方的關聯屬性,形成兩個一對多的關聯,如此才可以滿足資料庫正規化需求。多對多的關聯模型在 Entity Framework 2.0 上透過 FK Association 的支援,也可以成功的在 Entity Model 中建立,並在程式碼中使用。

 

多說無益,來做一次就知道囉。

 

1. 使用 Visual Studio 2010 建立一個主控台專案 (Console Application),並加入一個名稱為 SchoolDB 的 EDM 模型 (使用 Empty Model 方式)。

 

image

 

2. 打開 SchoolDB.edmx,進入 EDM Designer 工具,並建立三個 Entity,分別為 Students,Courses 以及 CourseRegistrations 三個 Entity,如圖所示:

 

image

 

其中的各個 Entity 的設定為:

 

Students 有 StudentID (型別為 String,Max Size = 50,為 Entity Key) 以及 Name (型別為 String,Max Size = 50),作為儲存學生資訊用。
Courses 有 CourseID (型別為 String,Max Size = 50,為 Entity Key) 以及 Name (型別為 String,Max Size = 250),作為儲存課程資訊用。
CourseRegistrations 有 Score (型別為 int),作為選課資料。

 

3. 在 EDM Designer 的空白處按右鍵,選擇 Add –> Association:

 

image

 

並在設定關聯時指定 Students 和 CourseRegistrations Entity 的關聯資訊,並且勾選 Add foreign key properties to the [table] entity 以建立具參考完整性的 FK Association:

 

image

 

關聯建立完成後,EDM 會顯示出 Students 與 CourseRegistrations 之間的關聯性:

 

image

 

然後依相同的方法,建立 Courses 與 CourseRegistrations Entity 之間的關聯性,完成後的 EDM 模型如下圖所示:

 

image

 

4. 將 CourseRegistration 的 StudentsStudentID 與 CoursesCourseID 更名為 StudentID 與 CourseID,這樣才更貼近資料庫的設計方式,請在 CourseRegistrations Entity 上選取 StudentsStudentID 並更改名稱 (或者在屬性視窗中修改 Name 屬性) 即可,並按相同的方法將 CoursesCourseID 更名為 CourseID,完成結果如下圖:

 

image

 

接著為了要符合 Entity 一定至少要有一個 Entity Key 的情況下,我們可以將 StudentID 和 CourseID 都設定為 Entity Key,設定方法為在屬性視窗中找到 Entity Key 屬性並設為 true 即可:

 

image

 

5. 在 EDM Designer 的空白處按右鍵,選擇 Generate Database from Model…

 

image

 

然後按 Generate Database Wizard 的指示設定與資料庫的連線,若有不清楚的地方,可以參考 [VS2010] ADO.NET Entity Framework 新功能:模型優先設計 (Model First Design) 一文的說明,將 Model 建到資料庫中。

 

image

 

6. 將下列程式碼貼到 Program.cs 中:

 

static void Main(string[] args)
{
    using (SchoolDBContainer context = new SchoolDBContainer())
    {
        Students studentA = new Students() { Name = "S1", StudentID = "110023" };
        Students studentB = new Students() { Name = "S2", StudentID = "110024" };
        Students studentC = new Students() { Name = "S3", StudentID = "110025" };

        Courses courseA = new Courses() { CourseID = "C1", Name = "Math" };
        Courses courseB = new Courses() { CourseID = "C2", Name = "Chinese" };
        Courses courseC = new Courses() { CourseID = "C3", Name = "Programming Language" };

        // add primary data.
        context.StudentsSet.AddObject(studentA);
        context.StudentsSet.AddObject(studentB);
        context.StudentsSet.AddObject(studentC);
        context.CoursesSet.AddObject(courseA);
        context.CoursesSet.AddObject(courseB);
        context.CoursesSet.AddObject(courseC);

        // add course registration data.
        studentA.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C3" });
        studentB.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C3" });
        studentC.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C2" });
        studentC.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C3" });
        courseA.CourseRegistrations.Add(new CourseRegistrations() { StudentID = "110023" });
        courseA.CourseRegistrations.Add(new CourseRegistrations() { StudentID = "110024" });
        courseA.CourseRegistrations.Add(new CourseRegistrations() { StudentID = "110025" });

        context.SaveChanges();

        // query 1: lookup student registered courses
        var q1 = from s in context.StudentsSet
                    join r in context.CourseRegistrationsSet on s.StudentID equals r.StudentID
                    join c in context.CoursesSet on r.CourseID equals c.CourseID
                    where s.Name == "S1"
                    select new { StudentName = s.Name, CourseName = c.Name };

        Console.WriteLine("Which courses are registered by Student S1: ");
        foreach (var item in q1)
            Console.WriteLine("Course: {0}", item.CourseName);

        // query 2: lookup who register the course.
        var q2 = from s in context.StudentsSet
                 join r in context.CourseRegistrationsSet on s.StudentID equals r.StudentID
                 join c in context.CoursesSet on r.CourseID equals c.CourseID
                 where c.Name == "Programming Language"
                 select new { StudentName = s.Name, CourseName = c.Name };

        Console.WriteLine("Who is registered course 'Programming Language': ");
        foreach (var item in q2)
            Console.WriteLine("Student: {0}", item.StudentName);

        Console.ReadLine();
    }
}

 

 

 

上面的程式碼分為兩部份,第一部份是插入資料,依據一般多對多資料庫的作法,兩邊主資料表都必須要先有資料,所以一開始先建立好主資料表中的資料:

 

Students studentA = new Students() { Name = "S1", StudentID = "110023" };
Students studentB = new Students() { Name = "S2", StudentID = "110024" };
Students studentC = new Students() { Name = "S3", StudentID = "110025" };

Courses courseA = new Courses() { CourseID = "C1", Name = "Math" };
Courses courseB = new Courses() { CourseID = "C2", Name = "Chinese" };
Courses courseC = new Courses() { CourseID = "C3", Name = "Programming Language" };

// add primary data.
context.StudentsSet.AddObject(studentA);
context.StudentsSet.AddObject(studentB);
context.StudentsSet.AddObject(studentC);
context.CoursesSet.AddObject(courseA);
context.CoursesSet.AddObject(courseB);
context.CoursesSet.AddObject(courseC);

 

接著在關聯表中建立兩者的關聯資料,在 Entity Framework 2.0 中,我們可以在 Students 的 CourseRegistrationSet 集合中插入資料,也可以在 Courses 的 CourseRegistrationSet 集合加入資料,Entity Framework 會自動在相對的 Entity 方向中加入該物件的 Key 資料 (這也是 Entity Framework 的 Foreign Key Association 最值得一看的地方),例如下列程式碼:

// add course registration data from Student Entity
studentA.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C3" }); // Can set CourseID only, the student key will be applied by studentA’s key.
studentB.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C3" }); // Can set CourseID only, the student key will be applied by studentB’s key.
studentC.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C2" }); // Can set CourseID only, the student key will be applied by studentC’s key.
studentC.CourseRegistrations.Add(new CourseRegistrations() { CourseID = "C3" }); // Can set CourseID only, the student key will be applied by studentC’s key.

// add course registration data from Order Entity
courseA.CourseRegistrations.Add(new CourseRegistrations() { StudentID = "110023" }); // Can set StudentID only, the course key will be applied by CourseA’s key.
courseA.CourseRegistrations.Add(new CourseRegistrations() { StudentID = "110024" }); // Can set StudentID only, the course key will be applied by CourseA’s key.
courseA.CourseRegistrations.Add(new CourseRegistrations() { StudentID = "110025" }); // Can set StudentID only, the course key will be applied by CourseA’s key.

 

以上的新增資料程式碼一定要在呼叫 ObjectContext.SaveChanges() 後才會生效,所以記得一定要呼叫它。

 

最後是使用 LINQ 在三個資料表之間串接以查詢資料的程式碼,查詢 1 為列出學生 S1 所選的課程,而查詢 2 為列出誰選了 Programming Language 這門課:

 

// query 1: lookup student registered courses
var q1 = from s in context.StudentsSet
         join r in context.CourseRegistrationsSet on s.StudentID equals r.StudentID
         join c in context.CoursesSet on r.CourseID equals c.CourseID

         where s.Name == "S1"
         select new { StudentName = s.Name, CourseName = c.Name };

// query 2: lookup who register the course.
var q2 = from s in context.StudentsSet
         join r in context.CourseRegistrationsSet on s.StudentID equals r.StudentID
         join c in context.CoursesSet on r.CourseID equals c.CourseID

         where c.Name == "Programming Language"
         select new { StudentName = s.Name, CourseName = c.Name };

 

7. 編譯它並執行,你應該可以看到這樣的視窗畫面:

 

image

 

表示程式已正常運作,此時到資料庫中就可以看到剛用程式碼插入的資料。

 

以上就是 Entity Framework 實作多對多的關聯的示範,希望對你有幫助。

微軟實戰課程日


DotBlogs Tags: ADO.NET VS2010

關連文章

[VS2010] ADO.NET Entity Framework: 由 Entity Object 執行 SQL 指令

[VS2010] Visual Studio 2010 與 Windows Azure: 準備篇 (1) - 建立 Windows Azure 帳戶

[VS2010] ADO.NET Entity Framework: 在永續儲存無知物件實作關聯

[VS2010] ADO.NET Entity Framework 新功能:外來鍵的支援 (Foreign Key Support)

[VS2010] ADO.NET Entity Framework: 解構永續儲存無知物件

[VS2010] ADO.NET Entity Framework 新功能:永續儲存無知物件 (Persistence-Ignorant Object) Overview

[VS2010] ADO.NET Entity Framework 新功能:模型優先設計 (Model First Design)

[VS2010] .NET Framework 4.0: ADO.NET Data Services 新功能

[VS2010] Visual Studio 的程式碼成員提示強化功能 (Intellisense Improvement)

[VS2010] ASP.NET 4.0 在 SEO 上的強化

回應

  • # re: [VS2010] ADO.NET Entity Framework: 建立多對多關聯模型 by add foreign dependence disabled

    hi,

    i encountered that the "add foreign dependence" item is disabled and cannot be selected. how to solve it?

    2010/2/8 上午 11:35 | 回覆

  • # re: [VS2010] ADO.NET Entity Framework: 建立多對多關聯模型 by 小朱

    Please confirm the key's data type is the same type.

    e.g.: Customers.CustomerID and Orders.CustomerID type must be integer.

    2010/2/8 下午 12:08 | 回覆

  • # re: [VS2010] ADO.NET Entity Framework: 建立多對多關聯模型 by bob

    to 小朱 :

    I've set them same type. but the add foreign dependence item is still gray.
    Can I send you email with the screen shot if possible?

    2010/2/22 上午 11:47 | 回覆

  • # re: [VS2010] ADO.NET Entity Framework: 建立多對多關聯模型 by 小朱

    I have traced the source code in Entity Framework Model Wizard, and found the wizard will check the version of your model, if version is 1.0 or unknown version, the foreign key check box will be disabled.
    You can create an empty model and try again.

    2010/2/22 下午 05:41 | 回覆

  • # re: [VS2010] ADO.NET Entity Framework: 建立多對多關聯模型 by bob

    to 小朱 :


    thanks for your answer. i will try it.

    2010/3/1 上午 10:48 | 回覆

登入後使用進階評論

Please add 7 and 4 and type the answer here: