1. 程式人生 > >Entity Framework多對多關聯對映的實現

Entity Framework多對多關聯對映的實現

Entity Framework是微軟官方提供的一個ORM解決方案,它有純正的血統,比NHibernate更容易使用並且與現有其它官方框架配合更加密切。

時代不斷的在發展變化,記得10年前還是ADO(配合ASP)的天下,後來微軟推出了ADO.NET,再後來推出了ADO.NET Entity Framework,可見微軟在.NET與資料庫互動領域的作為。

下面我將以Entity Framework(簡稱EF)來演示一下在C#當中如何使用好這個非常“爽”的ORM工具。我們以大家比較熟悉的模型—學生和課程的多對多的關係(學生可以選擇多門課程、課程有可能有多個學生選擇)來進行演示。

第一步,建立一個控制檯應用程式,起名為CodeFirstEF。我們簡單一點,儘量不參雜到其它技術來進行演示,將學習難度降低到最低。

建立一個控制檯應用程式沒有什麼好說的,這裡想簡單的提一下,使用EF有3種常用的模型,Database-First(資料庫優先)、Model-First(模型優先)、Code-First(程式碼優先)。其中前兩種,資料庫優先和模型優先是比較簡單的兩種模型。可以直接通過VS工具連線資料庫自動生成與資料庫互動的DbContext物件,這種模式有點像我們老早用過的不寫一行程式碼就能自動繫結GridView一樣(雖然有點誇張),雖然極大的提高了使用效率,但是靈活度欠缺。因此這篇部落格,主要演示程式碼優先模型,所以起名為CodeFirstEF。

第二步,在CodeFirstEF中建立一個資料夾Entity,裡面放置兩個模型實體(學生和課程):

namespace CodeFirstEF.Entity
{
    public enum Gender { Female, Male }

    public class Student
    {
        [Key]
        [DatabaseGeneratedAttribute(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
        public int StudentId { get; set; }
        public string StudentName { get; set; }
        public Gender Gender { get; set; }
        public DateTime? BirthDay { get; set; }

        public virtual ICollection<Subject> Subjects { get; set; }
    }
}
學生類上面有個列舉,用以區別性別。
namespace CodeFirstEF.Entity
{
    public class Subject
    {
        [Key]
        [DatabaseGeneratedAttribute(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
        public int SubjectId { get; set; }
        public string SubjectName { get; set; }

        public virtual ICollection<Student> Students { get; set; }
    }
}
課程和學生類中都有兩個虛屬性,分別表示學生的課程以及課程有哪些學生。

第三步,新增EntityFramework支援。

兩個Entity建好了,下面關鍵的要新增對EF的引用,這裡介紹一個強大的工具NuGet程式包。我們右鍵點選控制檯專案CodeFirstEF,選擇管理NuGet程式包,開啟下列彈出介面,選擇聯機,找到EntityFramework。


選擇安裝EntityFramework。


下載好EntityFramework會彈出視窗。


選擇我接受,很快就會裝好,裝好後如下所示。


點選關閉。這時我們已經為我們的專案添加了EntityFramework支援,可以看到版本號為6,這是目前的最新版本。

第四步,在CodeFirstEF控制檯專案下建立一個資料夾DAL,並建立一個數據庫操作類DataContext。

namespace CodeFirstEF.DAL
{
    public class DataContext : DbContext
    {
        public DataContext(string connectionName) : base(connectionName) { }

        public DbSet<Student> Students { get; set; }
        public DbSet<Subject> Subjects { get; set; }
    }
}
它繼承自DbContext,需要引用名稱空間 using System.Data.Entity。建構函式DbContext有一個引數connectionName,它是用於連線資料庫的名稱。這時我們切換到App.Config配置檔案下,新增connectionStrings節點配置,將資料庫連線的配置新增進去。
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <connectionStrings>
    <add name="codeFirstDb" connectionString="Data Source=.;uid=sa;pwd=123456;Database=CodeFirstDb;" providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>
可以看到裡面的codeFirstDb。

好了,這時候讓我們看一下整個專案的結構。


第五步,在Program.cs的Main方法中寫入測試程式碼。

namespace CodeFirstEF
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var db = new DataContext("codeFirstDb"))
            {
                //新增學生guwei4037
                if (!db.Students.Any(x => x.StudentName == "guwei4037"))
                {
                    db.Students.Add(new Student()
                    {
                        StudentName = "guwei4037",
                        Gender = Gender.Male,
                        BirthDay = new DateTime(1984, 11, 25),
                    });
                }

                //新增課程
                if (!db.Subjects.Any(x => x.SubjectName == "English" || x.SubjectName == "Mathmatics" || x.SubjectName == "Computer"))
                {
                    db.Subjects.AddRange(new Subject[] 
                    {
                        new Subject()
                        {
                            SubjectName="English",
                        },
                        new Subject()
                        {
                            SubjectName="Mathmatics",
                        },
                        new Subject()
                        {
                            SubjectName="Computer",
                        }
                    });
                }

                //找到guwei4037這個學生
                Student student = db.Students.FirstOrDefault(x => x.StudentName == "guwei4037");

                //找到數學和英語這兩門課程
                List<Subject> subjects = db.Subjects.Where(x => x.SubjectName == "Mathmatics" || x.SubjectName == "English").ToList();

                //給學生新增課程
                foreach (Subject subject in subjects)
                {
                    student.Subjects.Add(subject);
                }

                //讓課程知道有哪些學生選擇了它
                foreach (Subject subject in subjects)
                {
                    subject.Students.Add(student);
                }

                //刪除guwei4037這個學生其中的數學這門課程
                student.Subjects.Remove(db.Subjects.FirstOrDefault(x => x.SubjectName == "Mathmatics"));

                //儲存上述操作的結果
                db.SaveChanges();
            }
        }
    }
}
很簡單,註釋也很清晰。

執行一下整個控制檯專案,沒有報錯說明程式執行成功了。我們進資料庫檢視一下執行的情況。

這裡我們也可以利用VS工具來檢視,無須開啟SQL Server。






我們看到,EF替我們自動建立了資料庫CodeFirstDb,並且為我們建立了一張中間表,而且將資料都插入到了相應表中。

怎麼樣,EF相當強大吧?而且非常簡單好用,讓你寫程式碼有非常“爽”的感覺。真正的面向物件程式設計就是這麼簡單,不用再學習額外的SQL程式設計了。