此Web應用程序演示如何使用Entity Framework 6和Visual Studio 2015創建ASP.NET MVC 5應用程序。本教程使用“Code First ”即代碼先行。有關如何在“Code First”,“Database First”和“Model First”之間進行選擇,請參閱實體框架開發工作流程。如下:
-
Database First
如果已經擁有數據庫,Visual Studio中內置的Entity Framework設計器可以自動生成一個數據模型,該模型由對應於現有數據庫對象(如表和列)的類和屬性組成。有關數據庫結構,數據模型及映射之間的信息以XML格式存儲在.edmx文件中。實體框架設計器提供了一個可視化界面,您可以使用它來顯示和編輯.edmx文件。
-
Model First
如果您還沒有數據庫,則可以使用Visual Studio中的Entity Framework設計器在.edmx文件中創建一個模型。當模型建完后,可以執行.edmx文件來創建數據庫。
-
Code First
無論您是否擁有數據庫,都可以使用Code First。如果沒有數據庫,可以編寫類和對應於表和列的屬性。如果有數據庫,那么Entity Framework可以生成與現有表和列對應的類和屬性。如果使用Code First創建數據庫,則可以使用“migration(遷移)”來將數據庫部署到生產環境。當數據模型更改時,可以將更改部署到生產環境中,而不改變原有的數據。
Contoso University Web應用程序
此教程中構建的應用程序是一個簡單的web網站。
用戶可以查看和更新學生課程和教師信息。以下是創建的幾個頁面。
創建一個MVC Web應用程序
打開Visual Studio 2015並創建一個名為“EFDemo”的新C# Web項目。
在“ 新建ASP.NET項目”對話框中,選擇MVC模板。
更改身份驗證,改為不進行身份驗證(N)。
單擊“ 確定”創建項目。
做幾個簡單的更改。打開視圖\ Shared \ _Layout.cshtml,並進行以下更改,如下圖出顯示:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 5 <meta charset="utf-8" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>@ViewBag.Title - Contoso University </title> 8 @Styles.Render("~/Content/css") 9 @Scripts.Render("~/bundles/modernizr") 10 </head> 11 <body> 12 <div class="navbar navbar-inverse navbar-fixed-top"> 13 <div class="container"> 14 <div class="navbar-header"> 15 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 16 <span class="icon-bar"></span> 17 <span class="icon-bar"></span> 18 <span class="icon-bar"></span> 19 </button> 20 @Html.ActionLink("Contoso University ", "Index", "Home", new { area = "" }, new { @class = "navbar-brand" }) 21 </div> 22 <div class="navbar-collapse collapse"> 23 <ul class="nav navbar-nav"> 24 <li>@Html.ActionLink("主頁", "Index", "Home")</li> 25 <li>@Html.ActionLink("關於", "About", "Home")</li> 26 <li>@Html.ActionLink("學生", "Index", "Student")</li> 27 <li>@Html.ActionLink("課程", "Index", "Course")</li> 28 <li>@Html.ActionLink("老師", "Index", "Instructor")</li> 29 <li>@Html.ActionLink("部門", "Index", "Department")</li> 30 </ul> 31 </div> 32 </div> 33 </div> 34 <div class="container body-content"> 35 @RenderBody() 36 <hr /> 37 <footer> 38 <p>© @DateTime.Now.Year - Contoso University </p> 39 </footer> 40 </div> 41 42 @Scripts.Render("~/bundles/jquery") 43 @Scripts.Render("~/bundles/bootstrap") 44 @RenderSection("scripts", required: false) 45 </body> 46 </html>
在Views \ Home \ Index.cshtml中,使用以下代碼替換原有內容:

1 @{ 2 ViewBag.Title = "Home Page"; 3 } 4 5 <div class="jumbotron"> 6 <h1>Contoso University </h1> 7 </div> 8 <div class="row"> 9 <div class="col-md-4"> 10 <h2>Welcome to Contoso University </h2> 11 <p> 12 Contoso University is a sample application that 13 demonstrates how to use Entity Framework 6 in an 14 ASP.NET MVC 5 web application. 15 </p> 16 </div> 17 <div class="col-md-4"> 18 <h2>Build it from scratch</h2> 19 <p>You can build the application by following the steps in the tutorial series on the ASP.NET site.</p> 20 <p><a class="btn btn-default" href="http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/">See the tutorial »</a></p> 21 </div> 22 <div class="col-md-4"> 23 <h2>Download it</h2> 24 <p>You can download the completed project from the Microsoft Code Gallery.</p> 25 <p><a class="btn btn-default" href="http://code.msdn.microsoft.com/ASPNET-MVC-Application-b01a9fe8">Download »</a></p> 26 </div> 27 </div>
CTRL + F5運行網站,顯示如下:
安裝EF6
從工具菜單中單擊NuGet包管理器,然后單擊程序包管理器控制台。
在“ 管理器管理器”窗口中,輸入以下命令:
Install-Package EntityFramework
第二種方法:項目上右擊選擇管理NuGet程序包,選擇瀏覽輸入EntityFramework搜索,列表就會列出結果,選擇要安裝的包,點擊右側的安裝(我已經安裝過了,所以顯示的是"卸載")
這里顯示安裝的是Entity Framework6.1.3,NuGet將安裝最新版本的Entity Framework(不包括預發行版本)。
創建數據模型
接下來,您將為Contoso University 應用程序創建實體。將從以下三個實體開始:
Student
和Enrollment是一對多關系
,Course
和Enrollment也是一對多的關系
。換句話說,學生可以注冊任何數量的課程,課程可以被任何數量的學生注冊(學生和課程是多對多關系)。
接下來將為每個實體創建一個類。
Student
在Models文件夾中,創建一個名為Student.cs的類文件,並使用以下代碼替換模板代碼:

1 using System; 2 using System.Collections.Generic; 3 4 namespace EFDemo.Models 5 { 6 public class Student 7 { 8 public int ID { get; set; } 9 public string LastName { get; set; } 10 public string FirstMidName { get; set; } 11 public DateTime EnrollmentDate { get; set; } 12 13 public virtual ICollection<Enrollment> Enrollments { get; set; } 14 } 15 }
該ID
屬性將成為數據庫表的主鍵列。默認情況下,Entity Framework將一個名為ID
或classnameID的屬性作為主鍵。
該 Enrollments
屬性是導航屬性。導航屬性包含與該實體相關的其他實體信息。在這種情況下,Enrollments
持有與該Student
實體相關的Enrollment
實體的所有信息。
導航屬性通常被定義為virtual
使得它們可以利用某些實體框架功能,例如延遲加載。
如果導航屬性包含多個實體(如多對多或一對多關系),則其類型必須是list集合,例如ICollection
。
Enrollment
在Models文件夾中,創建Enrollment.cs並使用以下代碼替換現有代碼:

1 namespace EFDemo.Models 2 { 3 public enum Grade 4 { 5 A, B, C, D, F 6 } 7 8 public class Enrollment 9 { 10 public int EnrollmentID { get; set; } 11 public int CourseID { get; set; } 12 public int StudentID { get; set; } 13 public Grade? Grade { get; set; } 14 15 public virtual Course Course { get; set; } 16 public virtual Student Student { get; set; } 17 } 18 }
該EnrollmentID
屬性是主鍵; 該實體主鍵使用類名 加ID的命名方式
,而不是直接使用ID
。通常會選擇一種固定的命名方式,並在所有數據模型中使用。在這里,可以使用任意命名方式。在后面的教程中,將看到如何使用ID,
而不用classnameID,這樣
更容易在數據模型中實現繼承。
該Grade
屬性是一個枚舉類型。Grade
類型后的問號表示該Grade
屬性可以為空。空值表示未知等級或尚未分配。
該StudentID
屬性是一個外鍵,以及相應的導航屬性Student
。一個Enrollment
實體與一個Student
實體相關聯,因此該屬性只能保存一個Student
實體。
該CourseID
屬性也是一個外鍵,以及相應的導航屬性Course
。一個Enrollment
實體與一個Course
實體相關聯。
Course
在Models文件夾中,創建Course.cs,使用以下代碼替換模板代碼:

1 using System.Collections.Generic; 2 using System.ComponentModel.DataAnnotations.Schema; 3 4 namespace EFDemo.Models 5 { 6 public class Course 7 { 8 [DatabaseGenerated(DatabaseGeneratedOption.None)] 9 public int CourseID { get; set; } 10 public string Title { get; set; } 11 public int Credits { get; set; } 12 13 public virtual ICollection<Enrollment> Enrollments { get; set; } 14 } 15 }
該Enrollments
屬性是導航屬性。一個Course
實體可以有任意數量的Enrollment
實體。
DatabaseGenerated屬性,DatabaseGeneratedOption.None 設置允許輸入課程主鍵,而不是讓數據庫自動生成它(不是自動增長)。
創建數據庫上下文
右鍵單擊該項目解決方案資源管理器,然后單擊新建文件夾。命名新文件夾DAL(用於數據訪問層)。在該文件夾中創建一個名為SchoolContext.cs的類,代碼如下:

1 using EFDemo.Models; 2 using System.Data.Entity; 3 using System.Data.Entity.ModelConfiguration.Conventions; 4 5 namespace EFDemo.DAL 6 { 7 public class SchoolContext : DbContext 8 { 9 public SchoolContext() : base("SchoolDbContext") 10 { 11 12 } 13 14 public DbSet<Student> Students { get; set; } 15 public DbSet<Enrollment> Enrollments { get; set; } 16 public DbSet<Course> Courses { get; set; } 17 18 protected override void OnModelCreating(DbModelBuilder modelBuilder) 19 { 20 //阻止表名復數形式 21 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 22 } 23 } 24 }
指定連接字符串
連接字符串的名稱傳遞給構造函數
1 public SchoolContext() : base("SchoolDbContext") 2 { 3 }
Web.config文件中連接字符串如以下所示:
1 <connectionStrings> 2 <add name="SchoolDbContext" providerName="System.Data.SqlClient" connectionString="Data Source=ServerName;Initial Catalog=DatabaseName;Integrated Security=False;User Id=userid;Password=password;MultipleActiveResultSets=True" /> 3 </connectionStrings>
也可以傳遞連接字符串本身,而不是存儲在Web.config文件中。
如果不指定連接字符串或顯式指定名稱,則Entity Framework假定連接字符串名稱與類名稱相同。
指定實體集DbSet<>
每個實體集創建一個DbSet屬性。在EF中,實體集對應於數據庫的表,實體對應於表中的一行。
注意:可以省略聲明DbSet<Enrollment>
和DbSet<Course>
,它們將正常工作。EF將默認隱式地包含它們。
指定單數表名
OnModelCreating 方法中的modelBuilder.Conventions.Remove
語句阻止表名的復數形式。如果不這樣做,在數據庫中生成的表將被命名為復數形式(Students)。
設置使用測試數據來初始化數據庫
EF可以在應用程序運行時(或者當模型與現有數據庫不同步時)自動創建(或刪除或重新創建)數據庫。還可以編寫一個 Seed 方法,EF將在創建數據庫后自動調用,以便添加測試數據。
EF默認的是在數據庫不存在(CreateDatabaseIfNotExists)時創建數據庫(如果模型已更改,數據庫已經存在),則拋出異常。在本章中,將指定每當模型改變時,才刪除和重新創建數據庫。刪除和重新創建數據庫會丟失數據庫中原先的所有數據。這通常只在開發過程中使用,因為Seed方法將在數據庫重新創建時才運行,並重新添加測試數據。但在生產中環境,不可能每次更改數據庫時丟失所有數據。在下面的章節,將學習如何通過使用Code First migration(遷移)來更改數據庫,而不影響原來的數據。
CreateDatabaseIfNotExists
這是默認的數據庫初始化類,除非手動指定其他類。顧名思義,CreateDatabaseIfNotExists類僅在不存在數據庫時創建數據庫。
DropCreateDatabaseIfModelChanges
只要當模型類和表之間不匹配時,就會刪除數據庫並重新創建它。
DropCreateDatabaseAlways
每次運行應用程序時,都將刪除並重新創建數據庫,而不管它是否已經存在。
在DAL文件夾中,創建一個名為SchoolInitializer.cs的類,代碼如下:

1 using EFDemo.Models; 2 using System; 3 using System.Collections.Generic; 4 using System.Data.Entity; 5 6 namespace EFDemo.DAL 7 { 8 public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext> 9 { 10 protected override void Seed(SchoolContext context) 11 { 12 var students = new List<Student> 13 { 14 new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")}, 15 new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")}, 16 new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")}, 17 new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")}, 18 new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")}, 19 new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")}, 20 new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")}, 21 new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")} 22 }; 23 students.ForEach(s => context.Students.Add(s)); 24 context.SaveChanges(); 25 26 var courses = new List<Course> 27 { 28 new Course{CourseID=1050,Title="Chemistry",Credits=3,}, 29 new Course{CourseID=4022,Title="Microeconomics",Credits=3,}, 30 new Course{CourseID=4041,Title="Macroeconomics",Credits=3,}, 31 new Course{CourseID=1045,Title="Calculus",Credits=4,}, 32 new Course{CourseID=3141,Title="Trigonometry",Credits=4,}, 33 new Course{CourseID=2021,Title="Composition",Credits=3,}, 34 new Course{CourseID=2042,Title="Literature",Credits=4,} 35 }; 36 courses.ForEach(s => context.Courses.Add(s)); 37 context.SaveChanges(); 38 39 var enrollments = new List<Enrollment> 40 { 41 new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A}, 42 new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C}, 43 new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B}, 44 new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B}, 45 new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F}, 46 new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F}, 47 new Enrollment{StudentID=3,CourseID=1050}, 48 new Enrollment{StudentID=4,CourseID=1050,}, 49 new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F}, 50 new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C}, 51 new Enrollment{StudentID=6,CourseID=1045}, 52 new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A}, 53 }; 54 enrollments.ForEach(s => context.Enrollments.Add(s)); 55 context.SaveChanges(); 56 } 57 } 58 }
該Seed方法用於添加測試數據。
指定數據庫初始化程序
打開Web.config文件,添加代碼如下:
1 <entityFramework> 2 3 <contexts> 4 <context type="EFDemo.DAL.SchoolContext, EFDemo"> 5 <databaseInitializer type="EFDemo.DAL.SchoolInitializer, EFDemo" /> 6 </context> 7 </contexts> 8 9 <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework"> 10 <parameters> 11 <parameter value="mssqllocaldb" /> 12 </parameters> 13 </defaultConnectionFactory> 14 <providers> 15 <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> 16 </providers> 17 </entityFramework>
或者
1 <appSettings> 2 <add key = "DatabaseInitializerForType EFDemo.DAL.SchoolContext,EFDemo" value = "EFDemo.DAL.SchoolInitializer,EFDemo" /> 3 </appSettings>
它必須以預定義值DatabaseInitializerForType開頭,后跟一個空格,然后是上下文類的全名稱。
在context節點,type
指定上下文類的全名稱,后面是程序集的名稱。當你不想EF使用初始化,您可以給context
元素設置一個屬性:disableDatabaseInitialization="true"
。
作為在Web.config文件中設置初始化程序的替代方法是在Global.asax.cs文件中通過執行代碼來設置:
1 protected void Application_Start() 2 { 3 AreaRegistration.RegisterAllAreas(); 4 FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); 5 RouteConfig.RegisterRoutes(RouteTable.Routes); 6 BundleConfig.RegisterBundles(BundleTable.Bundles); 7 8 //指定數據庫初始化程序 9 Database.SetInitializer(new SchoolInitializer()); 10 }
SetInitializer() 方法接受數據庫初始化程序類的實例,並將其設置為當前應用程序數據庫初始化程序。設置數據庫初始化程序時,不會立即調用。當第一次使用上下文(SchoolContext)時,它將被調用。
當首次訪問數據庫時,Entity Framework會將數據庫與模型進行比較,如果有差異,將刪除並重新創建數據庫。
如果在數據庫初始化時,不想使用延遲加載,可以使用Initialize()方法覆蓋。如果模型復雜且初始化需要很多時間,可以顯式調用數據庫初始化程序。這樣就可以在某個已知步驟執行初始化(並且可以向用戶顯示等待消息)。使用Initialize()方法,代碼如下所示:
1 Database.Initialize(false);//立刻初始化數據,不延遲加載
在上述代碼中,數據庫將在調用Initialize()方法后立即創建,而不是延遲加載。Initialize()方法接受一個布爾類型的參數,用於控制是否重新執行初始化。指定false,如果已經執行,則跳過初始化過程。指定true,將重新初始化,即使它已被初始化。
有時可能不希望執行數據庫初始化邏輯。在這種情況下,可以通過將null傳遞給SetInitializer()方法,則將跳過初始化。
1 Database.SetInitializer<SchoolContext>(null);
注意:如果將應用程序部署到生產環境,則必須刪除或禁用初始化代碼。
創建自定義數據庫初始化程序
在上述示例中,使用內置的數據庫初始化程序。還可以通過實現IDatabaseInitializer<>接口來創建自定義數據庫初始化程序。需要實現IDatabaseInitializer<>接口的InitializeDatabase()方法,並編寫自己的數據庫創建邏輯。以下代碼顯示了InitializeDatabase()方法的示例實現:
1 public class SchoolInitializer : IDatabaseInitializer<SchoolContext> 2 { 3 #region 繼承 IDatabaseInitializer<SchoolContext>接口,實現InitializeDatabase()方法 4 public void InitializeDatabase(SchoolContext context) 5 { 6 //判斷數據庫是否已經存在 7 if (context.Database.Exists()) 8 { 9 //數據庫模式是否與模型兼容 10 if (!context.Database.CompatibleWithModel(true)) 11 { 12 context.Database.Delete(); 13 } 14 } 15 context.Database.Create(); 16 //使用自定義初始值設置來執行自定義任務 17 context.Database.ExecuteSqlCommand("CREATE TABLE CS_DATA([Id] int identity(1,1) primary key, [Name] NVARCHAR(50) not null)"); 18 } 19 #endregion 20 }
注意:代碼使用ExecuteSqlCommand()方法來創建不屬於模型的表。雖然我們在示例中不使用該表,但它說明了如何使用自定義初始化設置來執行自定義任務。
創建Student控制器和視圖
現在,將創建一個頁面來顯示數據,請求數據的過程將自動觸發數據庫的創建。先創建一個新的控制器。但是在這之前,需要重新生成整個項目,因為我們將使用MVC腳手架自動生成CRUD頁面。
- 右鍵單擊解決方案資源管理器中的Controllers文件夾,選擇添加,然后單擊控制器。
-
在彈出的對話框中,選擇包含視圖的MVC 5控制器(使用Entity Framework)。
-
在“添加控制器”對話框中,進行以下選擇,然后單擊“ 添加”:
- 模特類:學生(Student(EFDemo.Models))。(如果在下拉列表中沒有看到此選項,則重新生成項目,然后重試)
- 數據上下文類:SchoolContext(EFDemo.DAL)。
- 控制器名稱:StudentController。
-
其他保留默認值。
當單擊添加時,將創建一個StudentController.cs文件和一組與控制器配合使用的視圖(.cshtml文件)。
4.Visual Studio打開Controllers \ StudentController.cs文件。您看到已經創建了一個實例化數據庫上下文對象的類變量:
1 private SchoolContext db = new SchoolContext();
該Index
操作方法通過數據庫上下文實例的屬性查詢出所有學生信息:
1 // GET: Student 2 public ActionResult Index() 3 { 4 return View(db.Students.ToList()); 5 }
該學生\ Index.cshtml視圖顯示此列表中的表:

1 @model IEnumerable<EFDemo.Models.Student> 2 3 @{ 4 ViewBag.Title = "Index"; 5 } 6 7 <h2>Index</h2> 8 9 <p> 10 @Html.ActionLink("Create New", "Create") 11 </p> 12 <table class="table"> 13 <tr> 14 <th> 15 @Html.DisplayNameFor(model => model.LastName) 16 </th> 17 <th> 18 @Html.DisplayNameFor(model => model.FirstMidName) 19 </th> 20 <th> 21 @Html.DisplayNameFor(model => model.EnrollmentDate) 22 </th> 23 <th></th> 24 </tr> 25 26 @foreach (var item in Model) { 27 <tr> 28 <td> 29 @Html.DisplayFor(modelItem => item.LastName) 30 </td> 31 <td> 32 @Html.DisplayFor(modelItem => item.FirstMidName) 33 </td> 34 <td> 35 @Html.DisplayFor(modelItem => item.EnrollmentDate) 36 </td> 37 <td> 38 @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | 39 @Html.ActionLink("Details", "Details", new { id=item.ID }) | 40 @Html.ActionLink("Delete", "Delete", new { id=item.ID }) 41 </td> 42 </tr> 43 } 44 45 </table>
5.按CTRL + F5運行項目。(或Index.cshtml文件右擊在瀏覽器中查看或者直接按F5)
單擊“ 學生”選項卡以查看Seed()
方法插入的測試數據。
查看數據庫
當您運行學生頁面,應用程序嘗試訪問數據庫時,EF看到沒有數據庫,因此它創建了一個數據庫,然后運行Seed()方法添加測試數據。
因為正在使用DropCreateDatabaseIfModelChanges
初始化程序,現在可以對Student
類進行更改,再次運行應用程序,將自動重新創建數據庫。例如,如果向Student
該類添加一個屬性EmailAddress
,再次運行“學生”頁面,然后再次查看該表,將看到一個新列EmailAddress
。
1 public string EmailAddress { get; set; }
當重新運行時,可能會報這個錯:
解決辦法:先刪除原數據庫,勾選關閉現有連接,然后再運行程序。后面利用遷移就不需要手動刪除數據庫了。
然后查看數據庫,增加了新的列
約定
使用Entity Framework能夠創建一個完整的數據庫,發現我們只編寫了很少的代碼,因為Entity Framework有默認約定:
- 實體類名一般用作表名。
- 實體屬性名稱用於列名。
- 名稱
ID
或classnameID屬性被識別為主鍵屬性。 - 還有外鍵屬性,例如,
StudentID
用於Student
導航屬性。
當然我們可以將屬性顯式地標記為外鍵屬性等。將在本系列后面繼續學習。
參考:https://docs.microsoft.com/zh-cn/aspnet/