[VS2010] ADO.NET Entity Framework: 建立多對多關聯模型

[VS2010] ADO.NET Entity Framework: 建立多對多關聯模型

昨天才寫完 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 實作多對多的關聯的示範,希望對你有幫助。