用ASP.NET Core MVC 和 EF Core 构建Web应用 (一)
系統必備
- .NET Core 2.0.0 SDK?或更高版本。
- 已安裝 ASP.NET 和 Web 開發工作負載的 Visual Studio 2017 15.3 版或更高版本。
創建Web應用程序
打開 Visual Studio 并創建一個新 ASP.NET Core C# web 項目名為”ContosoUniversity”。
-
從文件菜單上,選擇新建 > 項目。
-
從左窗格中,選擇已安裝 > Visual C# > Web。
-
選擇“ASP.NET Core Web 應用程序”項目模板。
-
輸入ContosoUniversity作為名稱,然后單擊確定。
-
在 “新建 ASP.NET Core Web 應用程序” 對話框,選擇ASP.NET Core 2.1和?Web 應用程序 (模型-視圖-控制器)模板。
-
注意:本教程需要安裝 ASP.NET Core 2.0 和 EF Core 2.0 或更高版本。
-
請確保身份驗證設置為不進行身份驗,單擊“確定”。
?
修改頁面菜單布局和主頁
打開Views/Shared/_Layout.cshtml并進行以下更改:
-
將文件中的”ContosoUniversity”更改為”Contoso University”。 需要更改三個地方。
-
添加菜單項Students,Courses,Instructors,和Department,并刪除Contact菜單項。
在Views/Home/Index.cshtml,將文件的內容替換為以下代碼以將有關 ASP.NET 和 MVC 的內容替換為有關此應用程序的內容:
@{ViewData["Title"] = "Home Page"; }<div class="jumbotron"><h1>Contoso University</h1> </div> <div class="row"><div class="col-md-4"><h2>Welcome to Contoso University</h2><p>Contoso University is a sample application thatdemonstrates how to use Entity Framework Core in anASP.NET Core MVC web application.</p></div><div class="col-md-4"><h2>Build it from scratch</h2><p>You can build the application by following the steps in a series of tutorials.</p><p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial »</a></p></div><div class="col-md-4"><h2>Download it</h2><p>You can download the completed project from GitHub.</p><p><a class="btn btn-default" href="https://github.com/aspnet/Docs/tree/master/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code »</a></p></div> </div> View Code按 CTRL + F5 來運行該項目或從菜單選擇調試 > 開始執行不調試。 你會看到首頁和將通過這個教程創建的頁對應的選項卡。
?
數據訪問部分使用 EF Core
若要為項目添加 EF Core 支持,需要安裝相應的數據庫驅動包。 本教程使用 SQL Server,相關驅動包Microsoft.EntityFrameworkCore.SqlServer。 該包包含在Microsoft.AspNetCore.All?包中,因此不需要手動安裝。此包和其依賴項 (Microsoft.EntityFrameworkCore和Microsoft.EntityFrameworkCore.Relational) 一起提供 EF 的運行時支持。?
創建數據模型
創建 Contoso 大學應用程序的實體類,從以下三個實體類開始。
?
Student和Enrollment實體之間是一對多的關系,Course和Enrollment實體之間也是一個對多的關系。 換而言之,一名學生可以修讀任意數量的課程, 并且某一課程可以被任意數量的學生修讀。
在Models文件夾中,創建一個名為Student.cs的類文件并且將模板代碼替換為以下代碼。
1 using System; 2 using System.Collections.Generic; 3 4 namespace ContosoUniversity.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 ICollection<Enrollment> Enrollments { get; set; } 14 } 15 }ID屬性將成為對應于此類的數據庫表中的主鍵。 默認情況下,EF 將會將名為ID或classnameID的屬性解析為主鍵。
Enrollments屬性是導航屬性。 導航屬性中包含與此實體相關的其他實體。 在這個案例下,Student entity中的Enrollments屬性會保留所有與Student實體相關的Enrollment。 換而言之,如果在數據庫中有兩行描述同一個學生的修讀情況 (兩行的 StudentID 值相同,而且 StudentID 作為外鍵和某位學生的主鍵值相同),Student實體的Enrollments導航屬性將包含那兩個Enrollment實體。
如果導航屬性可以具有多個實體 (如多對多或一對多關系),那么導航屬性的類型必須是可以添加、 刪除和更新條目的容器,如ICollection<T>。 你可以指定ICollection<T>或實現該接口類型,如List<T>或HashSet<T>。 如果指定ICollection<T>,EF在默認情況下創建HashSet<T>集合。
在Models文件夾中,創建Enrollment.cs并且用以下代碼替換現有代碼:
1 namespace ContosoUniversity.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 Course Course { get; set; } 16 public Student Student { get; set; } 17 } 18 }EnrollmentID屬性將被設為主鍵; 此實體使用classnameID模式而不是如Student實體那樣直接使用ID。 通常情況下,你選擇一個主鍵模式,并在你的數據模型自始至終使用這種模式。 在這里,使用了兩種不同的模式只是為了說明你可以使用任一模式來指定主鍵。?
Grade屬性是enum。?Grade聲明類型后的?表示Grade屬性可以為 null。 評級為 null 和評級為零是有區別的 –null 意味著評級未知或者尚未分配。
StudentID屬性是一個外鍵,Student是與其且對應的導航屬性。?Enrollment實體與一個Student實體相關聯,因此該屬性只包含單個Student實體 (與前面所看到的Student.Enrollments導航屬性不同后,Student中可以容納多個Enrollment實體)。
CourseID屬性是一個外鍵,Course是與其對應的導航屬性。?Enrollment實體與一個Course實體相關聯。
如果一個屬性名為<導航屬性名><主鍵屬性名>,Entity Framework 就會將這個屬性解析為外鍵屬性(例如,Student實體的主鍵是ID,Student是Enrollment的導航屬性所以Enrollment實體中StudentID會被解析為外鍵)。 此外還可以將需要解析為外鍵的屬性命名為<主鍵屬性名>(例如,CourseID由于Course實體的主鍵所以CourseID也被解析為外鍵)。
在Models文件夾中,創建Course.cs并且用以下代碼替換現有代碼:
1 using System.Collections.Generic; 2 using System.ComponentModel.DataAnnotations.Schema; 3 4 namespace ContosoUniversity.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 ICollection<Enrollment> Enrollments { get; set; } 14 } 15 }Enrollments屬性是導航屬性。 一個Course實體可以與任意數量的Enrollment實體相關。
?
創建數據庫上下文
使得給定的數據模型與 Entity Framework 功能相協調的主類是數據庫上下文類。 可以通過繼承?Microsoft.EntityFrameworkCore.DbContext?類的方式創建此類。 在該類中你可以指定數據模型中包含哪些實體。 你還可以定義某些 Entity Framework 行為。 在此項目中將數據庫上下文類命名為SchoolContext。
在項目文件夾中,創建名為的文件夾Data。在Data文件夾創建名為SchoolContext.cs的類文件,并將模板代碼替換為以下代碼:
1 using ContosoUniversity.Models; 2 using Microsoft.EntityFrameworkCore; 3 4 namespace ContosoUniversity.Data 5 { 6 public class SchoolContext : DbContext 7 { 8 public SchoolContext(DbContextOptions<SchoolContext> options) : base(options) 9 { 10 } 11 12 public DbSet<Course> Courses { get; set; } 13 public DbSet<Enrollment> Enrollments { get; set; } 14 public DbSet<Student> Students { get; set; } 15 } 16 } View Code此代碼將為每個實體集創建DbSet屬性。 在 Entity Framework 中,實體集通常與數據表相對應,具體實體與表中的行相對應。
在這里可以省略DbSet<Enrollment>和DbSet<Course>語句,實現的功能沒有任何改變。 Entity Framework 會隱式包含這兩個實體因為Student實體引用了Enrollment實體、Enrollment實體引用了Course實體。
當數據庫創建完成后, EF 創建一系列數據表,表名默認和DbSet屬性名相同。 集合屬性的名稱一般使用復數形式,但不同的開發人員的命名習慣可能不一樣,開發人員根據自己的情況確定是否使用復數形式。在最后一個 DbSet 屬性之后添加以下代碼,對 DbContext 指定單數的表明來覆蓋默認的表名。
1 protected override void OnModelCreating(ModelBuilder modelBuilder) 2 { 3 modelBuilder.Entity<Course>().ToTable("Course"); 4 modelBuilder.Entity<Enrollment>().ToTable("Enrollment"); 5 modelBuilder.Entity<Student>().ToTable("Student"); 6 } View Code?
用依賴注入注冊上下文
ASP.NET Core 默認實現依賴注入。在應用程序啟動過程通過依賴注入注冊相關服務 (例如 EF 數據庫上下文)。 需要這些服務的組件 (如 MVC 控制器) 可以通過向構造函數添加相關參數來獲得對應服務。
若要將SchoolContext注冊為一種服務,打開Startup.cs,并將以下代碼添加到ConfigureServices方法中。
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.AddDbContext<SchoolContext>(options => 4 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 5 6 services.AddMvc(); 7 } View Code通過調用DbContextOptionsBuilder中的一個方法將數據庫連接字符串在配置文件中的名稱傳遞給上下文對象。 進行本地開發時,?ASP.NET Core 配置系統在appsettings.json文件中讀取數據庫連接字符串。
添加using語句引用ContosoUniversity.Data和Microsoft.EntityFrameworkCore命名空間,然后生成項目。
打開appsettings.json文件并添加連接字符串,如下所示。
1 { 2 "ConnectionStrings": { 3 "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true" 4 }, 5 "Logging": { 6 "IncludeScopes": false, 7 "LogLevel": { 8 "Default": "Warning" 9 } 10 } 11 }數據庫連接字符串指定使用 SQL Server LocalDB 數據庫。 LocalDB 是 SQL Server Express 數據庫引擎的輕量級版本,用于應用程序開發,不在生產環境中使用。 LocalDB 作為按需啟動并在用戶模式下運行的輕量級數據庫沒有復雜的配置。 默認情況下, LocalDB 在C:/Users/<user>目錄下創建.mdf數據庫文件。
用測試數據初始化數據庫
Entity Framework 已經為你創建了一個空數據庫。在本部分中編寫一個方法用于向數據庫填充測試數據,該方法會在數據庫創建完成之后執行。
此處將使用EnsureCreated方法來自動創建數據庫。?在Data文件夾中,創建名為的新類文件DbInitializer.cs并且將模板代碼替換為以下代碼,使得在需要時能創建數據庫并向其填充測試數據。
1 using ContosoUniversity.Models; 2 using System; 3 using System.Linq; 4 5 namespace ContosoUniversity.Data 6 { 7 public static class DbInitializer 8 { 9 public static void Initialize(SchoolContext context) 10 { 11 context.Database.EnsureCreated(); 12 13 // Look for any students. 14 if (context.Students.Any()) 15 { 16 return; // DB has been seeded 17 } 18 19 var students = new Student[] 20 { 21 new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")}, 22 new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")}, 23 new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")}, 24 new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")}, 25 new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")}, 26 new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")}, 27 new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")}, 28 new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")} 29 }; 30 foreach (Student s in students) 31 { 32 context.Students.Add(s); 33 } 34 context.SaveChanges(); 35 36 var courses = new Course[] 37 { 38 new Course{CourseID=1050,Title="Chemistry",Credits=3}, 39 new Course{CourseID=4022,Title="Microeconomics",Credits=3}, 40 new Course{CourseID=4041,Title="Macroeconomics",Credits=3}, 41 new Course{CourseID=1045,Title="Calculus",Credits=4}, 42 new Course{CourseID=3141,Title="Trigonometry",Credits=4}, 43 new Course{CourseID=2021,Title="Composition",Credits=3}, 44 new Course{CourseID=2042,Title="Literature",Credits=4} 45 }; 46 foreach (Course c in courses) 47 { 48 context.Courses.Add(c); 49 } 50 context.SaveChanges(); 51 52 var enrollments = new Enrollment[] 53 { 54 new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A}, 55 new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C}, 56 new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B}, 57 new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B}, 58 new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F}, 59 new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F}, 60 new Enrollment{StudentID=3,CourseID=1050}, 61 new Enrollment{StudentID=4,CourseID=1050}, 62 new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F}, 63 new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C}, 64 new Enrollment{StudentID=6,CourseID=1045}, 65 new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A}, 66 }; 67 foreach (Enrollment e in enrollments) 68 { 69 context.Enrollments.Add(e); 70 } 71 context.SaveChanges(); 72 } 73 } 74 } View Code這段代碼首先檢查是否有學生數據在數據庫中,如果沒有的話,就可以假定數據庫是新建的,然后使用測試數據進行填充。代碼中使用數組存放測試數據而不是使用List<T>集合是為了優化性能。
在Program.cs,修改Main方法,使得在應用程序啟動時能執行以下操作:
- 從依賴注入容器中獲取數據庫上下文實例。
- 調用 seed 方法,將上下文傳遞給它。
- Seed 方法完成此操作時釋放上下文。
現在首次運行該應用程序,創建數據庫并使用測試數據作為種子數據。 每當你更改你的數據模型,你可以刪除數據庫、 更新你的 Initialize 方法,然后使用上述方式更新新數據庫。 在之后的教程中,你將了解如何在數據模型更改時,只需修改數據庫而無需刪除重建數據庫。
創建控制器和視圖
用 Visual Studio 中的基架引擎添加一個 MVC 控制器和使用 EF 來查詢和保存數據的視圖。
CRUD 操作方法和視圖的自動創建被稱為基架。 基架與代碼生成不同,基架的代碼是一個起點,您可以修改基架以滿足自己需求,而你通常無需修改生成的代碼。 當你需要自定義生成代碼時,你使用一部分類或需求發生變化時重新生成代碼。
- 右鍵單擊解決方案資源管理器中的Controllers文件夾選擇添加 > 新搭建的基架項目。
-
在添加基架的對話框中:
-
選擇視圖使用 Entity Framework 的 MVC 控制器
-
單擊添加
-
-
在添加控制器對話框中:
-
在模型類選擇Student
-
在數據上下文類選擇SchoolContext
-
使用StudentsController作為默認名字
-
單擊添加
-
?
-
當你單擊添加后,Visual Studio 基架引擎創建StudentsController.cs文件和一組對應于控制器的視圖 (.cshtml文件) 。
(如果你之前手動創建數據庫上下文,基架引擎還可以自動創建。 你可以在添加控制器對話框中單擊右側的加號框數據上下文類來指定在一個新上下文類。然后,Visual Studio 將創建你的DbContext,控制器和視圖類。)
你會注意到控制器采用SchoolContext作為構造函數參數。
public class StudentsController : Controller{private readonly SchoolContext _context;public StudentsController(SchoolContext context){_context = context;}ASP.NET 依賴注入機制將會處理傳遞一個SchoolContext實例到控制器。
控制器包含Index操作方法用于顯示數據庫中的所有學生。 該方法從學生實體集中獲取學生列表,學生實體集則是通過讀取數據庫上下文實例中的Students屬性獲得:
public async Task<IActionResult> Index() {return View(await _context.Students.ToListAsync()); }Views/Students/Index.cshtml視圖使用table標簽顯示此列表:
1 @model IEnumerable<ContosoUniversity.Models.Student> 2 3 @{ 4 ViewData["Title"] = "Index"; 5 } 6 7 <h2>Index</h2> 8 9 <p> 10 <a asp-action="Create">Create New</a> 11 </p> 12 <table class="table"> 13 <thead> 14 <tr> 15 <th> 16 @Html.DisplayNameFor(model => model.LastName) 17 </th> 18 <th> 19 @Html.DisplayNameFor(model => model.FirstMidName) 20 </th> 21 <th> 22 @Html.DisplayNameFor(model => model.EnrollmentDate) 23 </th> 24 <th></th> 25 </tr> 26 </thead> 27 <tbody> 28 @foreach (var item in Model) { 29 <tr> 30 <td> 31 @Html.DisplayFor(modelItem => item.LastName) 32 </td> 33 <td> 34 @Html.DisplayFor(modelItem => item.FirstMidName) 35 </td> 36 <td> 37 @Html.DisplayFor(modelItem => item.EnrollmentDate) 38 </td> 39 <td> 40 <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> | 41 <a asp-action="Details" asp-route-id="@item.ID">Details</a> | 42 <a asp-action="Delete" asp-route-id="@item.ID">Delete</a> 43 </td> 44 </tr> 45 } 46 </tbody> 47 </table> View Code按 CTRL + F5 來運行該項目或從菜單選擇調試 > 開始執行(不調試)。
單擊學生選項卡以查看DbInitializer.Initialize插入的測試的數據。 你將看到Student選項卡鏈接在頁的頂部或在單擊右上角后的導航圖標中,具體顯示在哪里取決于瀏覽器窗口寬度。
? ??
?
約定
由于 Entity Framwork 有一定的約束條件,你只需要按規則編寫很少的代碼就能夠創建一個完整的數據庫,
-
DbSet類型的屬性用作表名。 實體未被DbSet屬性引用,實體類名稱用作表名稱。
-
實體屬性名稱用于列名稱。
-
ID 或 classnameID 命名的實體屬性被識別為主鍵屬性。
-
如果屬性名為?<導航屬性名> <主鍵名>將被解釋為外鍵屬性 (例如,StudentID對應Student導航屬性,Student實體的主鍵是ID,所以StudentID被解釋為外鍵屬性). 此外也可以將外鍵屬性命名為?<主鍵屬性名>?(例如,EnrollmentID,由于Enrollment實體的主鍵是EnrollmentID,因此被解釋為外鍵)。
約定行為可以被重寫。 例如,設置列名稱和將任何屬性設置為主鍵或外鍵。
異步代碼
異步編程是 ASP.NET Core 和 EF Core 的默認模式。
Web 服務器的可用線程是有限的,而在高負載情況下的可能所有線程都被占用。 當發生這種情況的時候,服務器就無法處理新請求,直到線程被釋放。 使用同步代碼時,可能會出現多個線程被占用但不能執行任何操作的情況,因為它們正在等待 I/O 完成。 使用異步代碼時,當進程正在等待 I/O 完成,服務器可以將其線程釋放用于處理其他請求。 因此,異步代碼使得服務器更有效地使用資源,并且該服務器可以無延遲地處理更多流量。
異步代碼在運行時,會引入的少量開銷,在低流量時對性能的影響可以忽略不計,但在針對高流量情況下潛在的性能提升是可觀的。
在下面的代碼中,async關鍵字,Task<T>返回值,await關鍵字,和ToListAsync方法使代碼異步執行。
public async Task<IActionResult> Index() {return View(await _context.Students.ToListAsync()); }-
async關鍵字用于告知編譯器該方法主體將生成回調并自動創建Task<IActionResult>返回對象。
-
返回類型Task<IActionResult>表示正在進行的工作返回的結果為IActionResult類型。
-
await關鍵字會使得編譯器將方法拆分為兩個部分。 第一部分是以異步方式結束已啟動的操作。 第二部分是當操作完成時注入調用回調方法的地方。
-
ToListAsync是由ToList方法的的異步擴展版本。
你使用 Entity Framework 編寫異步代碼時的一些注意事項:
-
只有導致查詢或發送數據庫命令的語句才能以異步方式執行。 包括?ToListAsync,?SingleOrDefaultAsync,和SaveChangesAsync。 不包括,操作IQueryable的語句,如var students = context.Students.Where(s => s.LastName == "Lilo")。
-
EF 上下文是線程不安全的: 請勿嘗試并行執行多個操作。 當調用異步 EF 方法時,始終使用await關鍵字。
-
如果你想要利用異步代碼的性能優勢,請確保你所使用的任何庫和包在它們調用導致 Entity Framework 數據庫查詢方法時也使用異步。?
總結
現已創建了一個使用 Entity Framework Core 和 SQL Server Express LocalDB 來存儲和顯示數據的簡單應用程序。 在下一篇中,將介紹如何執行基本的 CRUD (創建、 讀取、 更新、 刪除) 操作。
?
?
?
?
?*****************************
?*** Keep learning and growing. ***
?*****************************
轉載于:https://www.cnblogs.com/gangle/p/9190287.html
總結
以上是生活随笔為你收集整理的用ASP.NET Core MVC 和 EF Core 构建Web应用 (一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cesium的clock开始,结束,控制
- 下一篇: 从.Net到Java学习第四篇——spr