Thêm trường mới trong ASP.NET MVC 5

Trong phần này, bạn sẽ sử dụng Entity Framework Code First Migrations để di chuyển một số thay đổi đối với các lớp model để thay đổi được áp dụng cho cơ sở dữ liệu.

Theo mặc định, khi bạn sử dụng Entity Framework Code First để tự động tạo một cơ sở dữ liệu, như bạn đã làm trước đó trong hướng dẫn này, Code First thêm một bảng vào cơ sở dữ liệu để giúp theo dõi xem lược đồ của cơ sở dữ liệu có đồng bộ với các lớp model đã được tạo. Nếu chúng không được đồng bộ, Entity Framework sẽ đưa ra một lỗi. Điều này giúp bạn dễ dàng theo dõi các vấn đề tại thời điểm phát triển mà bạn có thể chỉ tìm thấy (do các lỗi ẩn) khi chạy.

Thiết lập Code First Migrations cho Model Changes

Điều hướng đến Solution Explorer. Nhấp chuột phải vào tệp Movies.mdf và chọn Delete để xóa cơ sở dữ liệu movie. Nếu bạn không thấy tệp Movies.mdf, hãy nhấp vào biểu tượng Show All Files được hiển thị bên dưới trong đường viền màu đỏ.

Biên dịch ứng dụng để đảm bảo không có lỗi.

Từ trình đơn Tools, nhấn NuGet Package Manager và sau đó là Package Manager Console.

Trong cửa sổ Package Manager Console tại PM> nhập Enable-Migrations -ContextTypeName MvcMovie.Models.MovieDBContext

Lệnh Enable-Migrations (hiển thị ở trên) tạo một tệp Configuration.cs trong một thư mục Migrations mới.

Visual Studio sẽ mở tệp Configuration.cs. Thay thế phương thức Seed trong tệp Configuration.cs bằng đoạn code sau:

protected override void Seed(MvcMovie.Models.MovieDBContext context) 
{context.Movies.AddOrUpdate( i => i.Title, new Movie 
{Title = "When Harry Met Sally", 
ReleaseDate = DateTime.Parse("1989-1-11"), 
Genre = "Romantic Comedy", Price = 7.99M }, 
new Movie { Title = "Ghostbusters ",
ReleaseDate = DateTime.Parse("1984-3-13"), 
Genre = "Comedy", Price = 8.99M }, 
new Movie { Title = "Ghostbusters 2", 
ReleaseDate = DateTime.Parse("1986-2-23"), 
Genre = "Comedy", Price = 9.99M }, new Movie { Title = "Rio Bravo", 
ReleaseDate = DateTime.Parse("1959-4-15"), 
Genre = "Western", Price = 3.99M } ); }

Di chuột qua đường kẻ nguệch ngoạc màu đỏ bên dưới Movie và nhấp vào Show Potential Fixes sau đó nhấp vào usingMvcMovie.Models;

Làm như vậy thêm câu lệnh sau:

using MvcMovie.Models;

Chú thích

Code First Migrations gọi phương thức Seed sau mỗi lần di chuyển (có nghĩa là gọi update database trong Package Manager Console), và phương thức này cập nhật các dòng đã được chèn vào hoặc chèn chúng nếu chúng chưa tồn tại.

Phương thức AddOrUpdate trong đoạn code sau thực hiện thao tác “upsert”:

context.Movies.AddOrUpdate(i => i.Title, 
new Movie { Title = "When Harry Met Sally", 
ReleaseDate = DateTime.Parse("1989-1-11"), 
Genre = "Romantic Comedy", Rating = "PG", Price = 7.99M }

Bởi vì phương thức Seed chạy với mọi lần di chuyển, bạn không thể chỉ chèn dữ liệu, bởi vì các dòng bạn đang cố gắng thêm sẽ tồn tại ở đó sau khi first migration tạo cơ sở dữ liệu. Thao tác “upsert” ngăn các lỗi xảy ra nếu bạn cố gắng chèn một dòng đã tồn tại, nhưng nó ghi đè bất kỳ thay đổi nào đối với dữ liệu mà bạn có thể đã thực hiện trong khi test ứng dụng. Với dữ liệu test trong một số bảng bạn có thể không muốn điều đó xảy ra: trong một số trường hợp khi bạn thay đổi dữ liệu trong khi test, bạn muốn các thay đổi của bạn vẫn còn sau khi cập nhật cơ sở dữ liệu. Trong trường hợp đó bạn muốn thực hiện thao tác chèn có điều kiện: chèn một dòng chỉ khi nó chưa tồn tại.

Tham số đầu tiên được chuyển đến phương thức AddOrUpdate chỉ định thuộc tính sử dụng để kiểm tra liệu một dòng đã tồn tại chưa. Đối với dữ liệu test movie mà bạn đang cung cấp, thuộc tính Title có thể được sử dụng cho mục đích này vì mỗi tiêu đề trong danh sách là duy nhất:

context.Movies.AddOrUpdate(i => i.Title,

Code này giả định rằng các tiêu đề là duy nhất. Nếu bạn thêm tiêu đề trùng lặp theo cách thủ công, bạn sẽ nhận được ngoại lệ sau vào lần tiếp theo bạn thực hiện migration. Trình tự chứa nhiều hơn một phần tử

Để biết thêm thông tin về phương thức AddOrUpdate, hãy xem Những điều cần quan tâm về phương thức AddOrUpdate của EF 4.3 ..

Nhấn CTRL-SHIFT-B để biên dịch dự án (Các bước sau sẽ thất bại nếu bạn không biên dịch tại thời điểm này.)

Bước tiếp theo là tạo một lớp DbMigration cho initial migration. Migration này tạo ra một cơ sở dữ liệu mới, đó là lý do tại sao bạn đã xóa tệp movie.mdf ở bước trước đó.

Trong cửa sổ Package Manager Console, nhập lệnh add-migration Initial để tạo migration Initial. Tên “Initial” là tùy ý và được sử dụng để đặt tên cho tệp migration được tạo.

Code First Migrations tạo một tệp lớp khác trong thư mục Migrations (với tên {DateStamp} _Initial.cs) và lớp này chứa code tạo lược đồ cơ sở dữ liệu. Tên tệp migration được định sẵn trước bằng dấu thời gian để trợ giúp đặt hàng. Kiểm tra tệp {DateStamp} _Initial.cs, nó chứa các hướng dẫn để tạo bảng Movies cho Movie DB phim. Khi bạn cập nhật cơ sở dữ liệu theo hướng dẫn bên dưới, tệp {DateStamp} _Initial.cs này sẽ chạy và tạo lược đồ DB. Sau đó, phương thức Seed sẽ chạy để điền DB với dữ liệu test.

Trong Package Manager Console, nhập lệnh update-database để tạo cơ sở dữ liệu và chạy phương thức Seed.

Nếu bạn nhận được một lỗi chỉ ra một bảng đã tồn tại và không thể được tạo ra, nó có lẽ là do bạn chạy ứng dụng sau khi đã xóa cơ sở dữ liệu và trước khi bạn thực thi update-database. Trong trường hợp đó, xóa tệp Movies.mdf và thử lại lệnh update-database. Nếu bạn vẫn gặp lỗi, hãy xóa thư mục migrations và nội dung, sau đó bắt đầu với hướng dẫn ở đầu trang này (xóa tệp Movies.mdf rồi chuyển sang Enable-Migrations). Nếu bạn vẫn nhận được lỗi, mở SQL Server Object Explorer và loại bỏ cơ sở dữ liệu từ danh sách.

Chạy ứng dụng và điều hướng đến /Movies URL. Dữ liệu gốc được hiển thị.

Thêm thuộc tính Rating vào Movie Model

Bắt đầu bằng cách thêm thuộc tính Rating mới vào lớp Movie hiện có. Mở tệp Models \ Movie.cs và thêm thuộc tính Rating như sau:

public string Rating { get; set; }

Lớp Movie hoàn chỉnh bây giờ trông giống như đoạn code sau:

public class Movie { public int ID { get; set; } 
\public string Title { get; set; } 
[Display(Name = "Release Date")] 
[DataType(DataType.Date)] 
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", 
ApplyFormatInEditMode = true)] 
public DateTime ReleaseDate { get; set; } 
public string Genre { get; set; } 
public decimal Price { get; set; }  
public string Rating { get; set; } }

Biên dịch  ứng dụng (Ctrl + Shift + B).

Vì bạn đã thêm một trường mới vào lớp Movie, bạn cũng cần phải cập nhật danh sách trắng liên kết để thuộc tính mới này sẽ được bao gồm. Cập nhật thuộc tính bind cho các phương thức Create và Edit để bao hàm thuộc tính Rating:

[Bind(Include = "ID,Title,ReleaseDate,Genre,Price,Rating")]

Bạn cũng cần phải cập nhật các mẫu xem để hiển thị, tạo và chỉnh sửa thuộc tính Rating mới trong view trình duyệt.

Mở tệp \Views\Movies\Index.cshtml và thêm tiêu đề cột <th>Rating</ th> ngay sau cột Price. Sau đó, thêm cột <td> ở gần cuối template để hiển thị giá trị @item.Rating. Dưới đây là những gì template Index.cshtml được cập nhật trông giống như sau:

@model IEnumerable<MvcMovie.Models.Movie> 
@{ ViewBag.Title = "Index"; } 
<h2>Index</h2> <p> @Html.ActionLink("Create New", "Create") 
@using (Html.BeginForm("Index", "Movies", FormMethod.Get)) 
{ <p> Genre: @Html.DropDownList("movieGenre", "All") 
Title:@Html.TextBox("SearchString") 
<input type="submit" value="Filter" /> </p> } 
</p> <table class="table"> <tr> <th> 
@Html.DisplayNameFor(model => model.Title)</th> 
<th> @Html.DisplayNameFor(model => model.ReleaseDate)</th> 
<th> @Html.DisplayNameFor(model => model.Genre) </th> 
<th> @Html.DisplayNameFor(model => model.Price) </th>  
<th>  @Html.DisplayNameFor(model => model.Rating)  </th> <th></th> 
</tr> @foreach (var item in Model) 
{ <tr> <td> @Html.DisplayFor(modelItem => item.Title) 
</td> <td> @Html.DisplayFor(modelItem => item.ReleaseDate) </td>
<td> @Html.DisplayFor(modelItem => item.Genre) </td> 
<td> @Html.DisplayFor(modelItem => item.Price) </td>  
<td> @Html.DisplayFor(modelItem => item.Rating)  </td> 
<td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
@Html.ActionLink("Details", "Details", new { id=item.ID }) | 
@Html.ActionLink("Delete", "Delete", 
new { id=item.ID }) </td> </tr> } </table>

Tiếp theo, mở tệp \Views\Movies\Create.cshtml và thêm trường Rating với đánh dấu highlighed sau. Điều này ám chỉ một văn bản để bạn có thể chỉ định một rating khi một movie mới được tạo ra.

<div class="form-group"> 
@Html.LabelFor(model => model.Price, new { @class = "control-label col-md-2" }) 
<div class="col-md-10"> 
@Html.EditorFor(model => model.Price,
new { htmlAttributes = new { @class = "form-control" } }) 
@Html.ValidationMessageFor(model => model.Price, "", 
new { @class = "text-danger" }) </div> </div>  <div class="form-group">  
@Html.LabelFor(model => model.Rating, new { @class = "control-label col-md-2" })  
<div class="col-md-10">  @Html.EditorFor(model => model.Rating, 
new { htmlAttributes = new { @class = "form-control" } })  
@Html.ValidationMessageFor(model => model.Rating, "", 
new { @class = "text-danger" })  </div>  
</div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> 
<input type="submit" value="Create" class="btn btn-default" /> 
</div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") 
</div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }

Bây giờ bạn đã cập nhật code ứng dụng để hỗ trợ thuộc tính Rating mới.

Chạy ứng dụng và điều hướng đến /Movies URL. Tuy nhiên, khi thực hiện điều này, bạn sẽ thấy một trong các lỗi sau:

Model ‘MovieDBContext’ đã thay đổi kể từ khi cơ sở dữ liệu được tạo. Cân nhắc sử dụng Code First Migrations để cập nhật cơ sở dữ liệu (https://go.microsoft.com/fwlink/?LinkId=238269).

Bạn thấy lỗi này vì lớp Movie model được cập nhật trong ứng dụng giờ đây khác với lược đồ của bảng Movie của cơ sở dữ liệu hiện có. (Không có cột Rating trong bảng cơ sở dữ liệu.)

Có một vài cách để giải quyết lỗi:

  1. Để Entity Framework tự động thả và tạo lại cơ sở dữ liệu dựa trên lược đồ lớp model mới. Cách tiếp cận này rất thuận tiện trong chu kỳ phát triển khi bạn đang phát triển tích cực trên cơ sở dữ liệu test; nó cho phép nhanh chóng phát triển lược đồ model và cơ sở dữ liệu với nhau. Nhược điểm, là bạn bị mất dữ liệu hiện có trong cơ sở dữ liệu – vì vậy bạn không muốn sử dụng phương pháp này trên cơ sở dữ liệu sản xuất! Sử dụng trình khởi tạo để tự động tạo một cơ sở dữ liệu với dữ liệu test thường là một cách hiệu quả để phát triển một ứng dụng. Để biết thêm thông tin về khởi tạo cơ sở dữ liệu Entity Framework, hãy xem Hướng dẫn ASP.NET MVC/Entity Framework.
  2. Sửa đổi rõ ràng lược đồ của cơ sở dữ liệu hiện có để nó khớp với các lớp model. Ưu điểm của phương pháp này là bạn giữ dữ liệu của mình. Bạn có thể thực hiện thay đổi này theo cách thủ công hoặc bằng cách tạo tập lệnh thay đổi cơ sở dữ liệu.
  3. Sử dụng Code First Migrations để cập nhật lược đồ cơ sở dữ liệu.

Đối với hướng dẫn này, chúng ta sẽ sử dụng Code First Migrations.

Cập nhật phương thức Seed để nó cung cấp một giá trị cho cột mới. Mở tệp Migrations\Configuration.cs và thêm trường Rating cho từng đối tượng Movie.

new Movie { Title = "When Harry Met Sally", 
ReleaseDate = DateTime.Parse("1989-1-11"), 
Genre = "Romantic Comedy",  Rating = "PG", Price = 7.99M },

Biên dịch giải pháp, và sau đó mở cửa sổ Package Manager Console và nhập vào lệnh sau:

add-migration Rating

Lệnh add-migration cho migration framework kiểm tra movie model hiện tại bằng lược đồ movie DB hiện tại và tạo code cần thiết để di chuyển DB sang model mới. Tên Raing là tùy ý và được sử dụng để đặt tên cho tệp migration. Bạn nên sử dụng tên có ý nghĩa cho bước migration.

Khi lệnh này kết thúc, Visual Studio sẽ mở tệp lớp định nghĩa lớp dẫn xuất DbMigration mới và trong phương thức Up bạn có thể thấy mã tạo cột mới.

public partial class AddRatingMig : DbMigration 
{ public override void Up() 
{ AddColumn("dbo.Movies", "Rating", c => c.String()); } 
public override void Down() 
{ DropColumn("dbo.Movies", "Rating"); } }

Biên dịch giải pháp, sau đó nhập lệnh update-database trong cửa sổ Package Manager Console.

Hình ảnh sau đây cho thấy kết quả đầu ra trong cửa sổ Package Manager Console (Ngày đánh dấu trước Rating sẽ khác nhau.)

Chạy lại ứng dụng và điều hướng đến /Movies URL. Bạn có thể thấy trường Rating mới.

Nhấp vào liên kết Create New để thêm movie mới. Lưu ý rằng bạn có thể thêm rating.

Nhấp vào Create. Movie mới, bao gồm cả rating, xuất hiện trong danh sách các movies:

Bây giờ dự án đang sử dụng migrations, bạn sẽ không cần phải drop cơ sở dữ liệu khi thêm một trường mới hoặc cập nhật lược đồ. Trong phần tiếp theo, chúng ta sẽ thực hiện nhiều thay đổi lược đồ hơn và sử dụng migrations để cập nhật cơ sở dữ liệu.

Bạn cũng nên thêm trường Rating vào Edit, Detail và Delete view templates.

Bạn có thể nhập lại lệnh “update-database” trong cửa sổ Package Manager Console và không có migration code nào chạy vì lược đồ khớp với model. Tuy nhiên, “update-database” sẽ chạy phương thức Seed một lần nữa và nếu bạn thay đổi bất kỳ dữ liệu Seed nào, các thay đổi sẽ bị mất vì phương thức Seed sẽ làm tăng dữ liệu.

Trong phần này bạn đã thấy cách bạn có thể sửa đổi các đối tượng model và giữ cho cơ sở dữ liệu đồng bộ với các thay đổi. Bạn cũng đã học cách tạo một cơ sở dữ liệu mới với dữ liệu mẫu để có thể thử kỹ hơn các kịch bản. Tiếp theo, hãy xem cách bạn có thể thêm logic xác thực vào các lớp model và cho phép một số quy tắc nghiệp vụ được thực thi.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *