本篇參考原文地址:
Creating an Entity Framework Data Model
說明:學習筆記參考原文中的流程,為了增加實際操作性,並能夠深入理解,部分地方根據實際情況做了一些調整;並且根據自己的理解做了一些擴展。
本人的學習環境: VS2017 + EF 6.1.3 + .NET 4.6.1
Step1 : 新建Web Application (我起名為EFTest)
選擇空白模板,但勾選 MVC 的Folder reference.
Step2: 通過Package Manager Console來安裝EF6
Step3: 新建一個LocalDB的空的測試數據庫
Step4: 新建一個 主頁面 作為基礎入口(在Controller目錄下新建一個空的Controller, 起名字就為Home 即可)
然后在Home Controller的Index Action上右鍵點擊來增加Index View: (就選空的View就可以,測試嘛,能簡單就簡單)
並將Home/Index View中修改為以下代碼:
@{ ViewBag.Title = "Hello EF6"; } <h2>Hello EF6</h2> <div> <ul> <li>@Html.ActionLink("Home", "Index", "Home")</li> <li>@Html.ActionLink("About", "About", "Home")</li> <li>@Html.ActionLink("Students", "Index", "Student")</li> <li>@Html.ActionLink("Courses", "Index", "Course")</li> <li>@Html.ActionLink("Instructors", "Index", "Instructor")</li> <li>@Html.ActionLink("Departments", "Index", "Department")</li> </ul> </div>
Step5: 在Web.config中增加 數據庫字符串;
<connectionStrings> <add name="SchoolContext" connectionString="Data Source=(localdb)\ProjectsV13;Initial Catalog=EFTest;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=True;ApplicationIntent=ReadWrite;MultiSubnetFailover=False;" providerName="System.Data.SqlClient"/> </connectionStrings>
注:可以點擊 SQLServer Object Explorer中的數據庫,然后在 屬性頁中,可以拷貝出該數據庫的連接字符串;
Step6: 正式開始EF學習測試相關的操作:
6.1 在Models目錄下新建 Student類
using System; using System.Collections.Generic; namespace EFTest.Models { public class Student { public int ID { get; set; } public string LastName { get; set; } public string FirstMidName { get; set; } public DateTime EnrollmentDate { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } }
6.2 在Models目錄下新建Course類
using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; namespace EFTest.Models { public class Course { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int CourseID { get; set; } public string Title { get; set; } public int Credits { get; set; } public virtual ICollection<Enrollment> Enrollments { get; set; } } }
6.3 在Models目錄下新建Enrollment類
namespace EFTest.Models { public enum Grade { A, B, C, D, F } public class Enrollment { public int EnrollmentID { get; set; } public int CourseID { get; set; } public int StudentID { get; set; } public Grade? Grade { get; set; } public virtual Course Course { get; set; } public virtual Student Student { get; set; } } }
通過類定義可以看到: 一個學生可以選修多個課程(有多個課程記錄),一個課程可以多個學生選修(有多個學生記錄);
一個課程記錄對應一個學生一門課程;
那么對於課程記錄,學生ID和課程ID就是外鍵;必須先存在某個學生,才可以有這個學生的課程記錄,也必須先存在某個課程,才可以由這個課程的課程記錄;
6.4 新建一個DAL文件夾(數據訪問層),新建 SchoolContext 類和SchoolInitializer類;
說明:SchoolContext類從DbContext繼承,並在構造函數中定義 數據庫字符串名:base("SchoolContext") ,然后通過DbSet<T>定義數據庫模型;
最后還重載了OnModelCreating方法,用來改變一些約束;(如果例子中的表名不用變復數,以及其它約束,比如改表名、列名等等);
using EFTest.Models; using System.Data.Entity; using System.Data.Entity.ModelConfiguration.Conventions; namespace EFTest.DAL { public class SchoolContext : DbContext { public SchoolContext() : base("SchoolContext") { } public DbSet<Student> Students { get; set; } public DbSet<Enrollment> Enrollments { get; set; } public DbSet<Course> Courses { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } } }
說明:SchoolInitializer類用來定義數據庫在初始化的時候需要做的一些事情;比如通過重載Seed方法預先放入一些數據等;
using System; using System.Collections.Generic; using EFTest.Models; namespace EFTest.DAL { public class SchoolInitializer : System.Data.Entity.DropCreateDatabaseIfModelChanges<SchoolContext> { protected override void Seed(SchoolContext context) { var students = new List<Student> { new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")}, new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")}, new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")}, new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")}, new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")}, new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")}, new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")}, new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")} }; students.ForEach(s => context.Students.Add(s)); context.SaveChanges(); var courses = new List<Course> { new Course{CourseID=1050,Title="Chemistry",Credits=3,}, new Course{CourseID=4022,Title="Microeconomics",Credits=3,}, new Course{CourseID=4041,Title="Macroeconomics",Credits=3,}, new Course{CourseID=1045,Title="Calculus",Credits=4,}, new Course{CourseID=3141,Title="Trigonometry",Credits=4,}, new Course{CourseID=2021,Title="Composition",Credits=3,}, new Course{CourseID=2042,Title="Literature",Credits=4,} }; courses.ForEach(s => context.Courses.Add(s)); context.SaveChanges(); var enrollments = new List<Enrollment> { new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A}, new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C}, new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B}, new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B}, new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F}, new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F}, new Enrollment{StudentID=3,CourseID=1050}, new Enrollment{StudentID=4,CourseID=1050,}, new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F}, new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C}, new Enrollment{StudentID=6,CourseID=1045}, new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A}, }; enrollments.ForEach(s => context.Enrollments.Add(s)); context.SaveChanges(); } } }
Step7: 定義數據庫初始化(本章不涉及數據庫遷移等,遷移以后再說)
第6步定義了上下文類以及初始化類,那么怎么讓應用在執行時候進行數據庫初始化動作?
有兩種方式:(只能選一種)
1、在Web.config 中定義:
在<entityFramework>節點中,增加<contexts>節點,分別定義<context>的type ,以及初始化<databaseInitializer>的type: (具體為啥這樣定義,只能以后看原理。。。)
<entityFramework> <contexts> <context type="EFTest.DAL.SchoolContext, EFTest"> <databaseInitializer type="EFTest.DAL.SchoolInitializer, EFTest" /> </context> </contexts> <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> <parameters> <parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> </providers> </entityFramework>
2、在Global.asax中執行初始化;在Application_Start()方法中,最后加上以下數據庫初始化代碼:
Database.SetInitializer<SchoolContext>(new SchoolInitializer());
注:第1種方法,在Home/Index頁面打開的時候是不會初始化數據庫的,因為還沒操作數據庫讀寫。。。下面加訪問頁面來進行數據庫讀取;
Step8: 增加一個 Student Controller , 直接選擇空白的就可以,通過敲入代碼來更多理解:
先為Student控制器加一個SchoolContext db , 然后把Student/Index的return View()改為如下:
using EFTest.DAL; using System.Linq; using System.Web.Mvc; namespace EFTest.Controllers { public class StudentController : Controller { private SchoolContext db = new SchoolContext(); // GET: Student public ActionResult Index() { return View(db.Students.ToList()); } } }
為Index Action加View, 選擇List模板 , Model 選Student , 然后再選擇好上下文類:
注:如果出現以下錯誤,則表示先要編譯一下,再建View
Step9: 執行起來看看 Student/Index 頁面:
也可以查看一下數據庫,已經新建成功:
學習整理總結:
1、根據ASP.NET MVC的概念,約定大於配置,EF6同樣存在;
數據類定義中,如果名為ID的,則自動為主鍵,如果為類名+ID的,也會自動為主鍵; 其他屬性名自動為列表;如果為int型,則會默認為自增長主鍵;
比較神奇的是外鍵的自動創建,這個約束描述起來好累,實際看看代碼和實際數據庫的外鍵就能明白;
(通過加 virtual 描述來實現延時數據加載,即程序用到這個屬性定義的數據時,才會去查數據庫)
2、 既然有約定就可以改變約定;
可以通過對屬性增加 Annotations注釋 來改變約定,例如:修改主鍵不要為自增加主鍵,改變列表,增加其他列為主鍵,設定字段長度等等;
如:[DatabaseGenerated(DatabaseGeneratedOption.None)]