ASP.NET Core: Razor Pages với Entity Framework Core trong ASP.NET Core - Hướng dẫn 1/8


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Đây là phần đầu tiên trong loạt hướng dẫn về cách sử dụng Entity Framework (EF) Core trong ứng dụng ASP.NET Core Razor Pages. Các hướng dẫn xây dựng một trang web cho một trường đại học Contoso hư cấu. Trang web bao gồm các chức năng như tuyển sinh, tạo khóa học và phân công người hướng dẫn. Hướng dẫn sử dụng cách tiếp cận mã đầu tiên. Để biết thông tin về cách làm theo hướng dẫn này bằng cách sử dụng phương pháp tiếp cận cơ sở dữ liệu đầu tiên, hãy xem vấn đề Github này.

Tải xuống hoặc xem ứng dụng đã hoàn thành. Hướng dẫn tải xuống.

Điều kiện tiên quyết

  • Nếu bạn chưa quen với Razor Pages, hãy xem qua loạt bài hướng dẫn Bắt đầu với Razor Pages trước khi bắt đầu loạt bài này.
  • Visual Studio 2022 đã cài đặt ASP.NET and web development.
  • Công cụ cơ sở dữ liệu: Các hướng dẫn Visual Studio sử dụng SQL Server LocalDB, một phiên bản của SQL Server Express chỉ chạy trên Windows.

Xử lý sự cố

Nếu bạn gặp sự cố mà bạn không thể giải quyết, hãy so sánh mã của bạn với dự án đã hoàn thành. Một cách hay để nhận trợ giúp là đăng câu hỏi lên StackOverflow.com, sử dụng tag ASP.NET Core hoặc tag EF Core.

Ứng dụng mẫu

Ứng dụng được xây dựng trong các hướng dẫn này là một trang web cơ bản của trường đại học. Người dùng có thể xem và cập nhật thông tin về sinh viên, khóa học và người hướng dẫn. Dưới đây là một số màn hình được tạo trong hướng dẫn.

Trang mục lục học sinh

Sinh viên Sửa trang

Kiểu giao diện người dùng của trang web này dựa trên các mẫu dự án tích hợp sẵn. Trọng tâm của hướng dẫn là cách sử dụng EF Core với ASP.NET Core chứ không phải cách tùy chỉnh giao diện người dùng.

Tùy chọn: Xây dựng bản tải xuống mẫu

Bước này là tùy chọn. Bạn nên xây dựng ứng dụng hoàn chỉnh khi gặp sự cố không thể giải quyết. Nếu bạn gặp sự cố mà bạn không thể giải quyết, hãy so sánh mã của bạn với dự án đã hoàn thànhHướng dẫn tải xuống.

Chọn ContosoUniversity.csproj để mở dự án.

  • Xây dựng dự án.
  • Trong Package Manager Console (PMC) chạy lệnh sau:
Update-Database

Chạy dự án để khởi tạo cơ sở dữ liệu.

Tạo dự án ứng dụng web

1. Khởi động Visual Studio 2022 và chọn Create a new project.

Tạo một dự án mới từ cửa sổ bắt đầu

2. Trong hộp thoại Create a new project, chọn ASP.NET Core Web App rồi chọn Next.

Tạo một ứng dụng web ASP.NET Core

3. Trong hộp thoại Configure your new project, nhập ContosoUniversity cho phần Project name. Điều quan trọng là phải đặt tên cho dự án là ContosoUniversity, bao gồm cả cách viết hoa phù hợp, để các namespace sẽ khớp khi bạn sao chép và dán mã ví dụ.

4. Chọn Next.

5. Trong hộp thoại Additional information, chọn .NET 6.0 (Long-term support) rồi chọn Create.

Thông tin thêm

Thiết lập style cho trang web

Sao chép và dán đoạn mã sau vào file Pages/Shared/_Layout.cshtml:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
    <link rel="stylesheet" href="~/ContosoUniversity.styles.css" asp-append-version="true" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
                    <ul class="navbar-nav flex-grow-1">                        
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2021 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

File layout sẽ thiết lập header, foooter và menu cho trang web. Đoạn code trên thực hiện các thay đổi sau:

  • Mỗi lần xuất hiện "ContosoUniversity" thành "Contoso University". Có ba lần xuất hiện.
  • Các mục menu HomePrivacy sẽ bị xóa.
  • Các mục nhập được thêm cho About, Students, Courses, Instructors và Departments.

Trong Pages/Index.cshtml, thay thế nội dung của file bằng đoạn code sau:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
@*                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
*@                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
@*                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
*@                </p>
            </div>
        </div>
    </div>
</div>

Đoạn code trên thay thế văn bản về ASP.NET Core bằng văn bản về ứng dụng này.

Chạy ứng dụng để xác minh rằng trang chủ xuất hiện.

Mô hình dữ liệu

Các phần sau tạo một mô hình dữ liệu:

Biểu đồ mô hình dữ liệu Khóa học-Tuyển sinh-Sinh viên

Mô tả: Một sinh viên có thể đăng ký vào bất kỳ số lượng khóa học nào và một khóa học có thể có bất kỳ số lượng sinh viên nào đăng ký vào đó.

Thực thể Student

Sơ đồ thực thể sinh viên

  • Tạo một thư mục có tên Models trong thư mục dự án.
  • Tạo Models/Student.cs và đưa vào đoạn code sau:
    namespace ContosoUniversity.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 ICollection<Enrollment> Enrollments { get; set; }
        }
    }
    

Property ID trở thành cột khóa chính của bảng cơ sở dữ liệu tương ứng với lớp này. Theo mặc định, EF Core diễn giải một property được đặt tên ID hoặc classnameID làm khóa chính. Vì vậy, tên thay thế được nhận dạng tự động cho khóa chính của lớp Student là StudentID. Để biết thêm thông tin, hãy xem EF Core - Keys.

Property Enrollments là property điều hướng. Property điều hướng giữ các thực thể khác có liên quan đến thực thể này. Trong trường hợp này, property Enrollments của thực thể Student chứa tất cả  Enrollment các thực thể có liên quan đến Student đó. Ví dụ: nếu hàng Student trong cơ sở dữ liệu có hai hàng Enrollment, thì property điều hướng Enrollments sẽ chứa hai thực thể Enrollment đó.

Trong cơ sở dữ liệu, một hàng Enrollment có liên quan đến một hàng Student nếu cột StudentID của nó chứa giá trị ID của sinh viên. Ví dụ: giả sử hàng Student có ID=1. Các hàng liên quan Enrollment sẽ có  StudentID = 1. StudentID là một khóa ngoại trong bảng Enrollment.

Property Enrollments được định nghĩa là ICollection<Enrollment> vì có thể có nhiều thực thể Enrollment liên quan. Các loại collection khác có thể được sử dụng, chẳng hạn như List<Enrollment> hoặc  HashSet<Enrollment>. Khi ICollection<Enrollment> được sử dụng, EF Core sẽ tạo một collection HashSet<Enrollment> theo mặc định.

Thực thể Enrollment

Sơ đồ thực thể đăng ký

Tạo Models/Enrollment.cs và đưa vào đoạn code sau:

using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.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; }
        [DisplayFormat(NullDisplayText = "No grade")]
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
}

Property EnrollmentID là khóa chính; thực thể này sử dụng mẫu classnameID thay vì ID chính nó. Đối với model dữ liệu production, nhiều nhà phát triển chọn một mẫu và sử dụng mẫu đó một cách nhất quán. Hướng dẫn này sử dụng cả hai chỉ để minh họa rằng cả hai đều hoạt động. Sử dụng ID mà không có classname sẽ giúp dễ dàng thực hiện một số loại thay đổi mô hình dữ liệu.

Property Grade là một enum. Dấu chấm hỏi sau khi khai báo kiểu Grade chỉ ra rằng property Grade là nullable. Một điểm mà là null thì khác với điểm 0—null có nghĩa là điểm chưa được biết hoặc chưa được gán.

Property StudentID là khóa ngoại và property điều hướng tương ứng là Student. Một thực thể Enrollment được liên kết với một thực thể Student, vì vậy property chứa một thực thể Student duy nhất.

Property CourseID là khóa ngoại và property điều hướng tương ứng là Course. Một thực thể Enrollment được liên kết với một thể Course.

EF Core hiểu một property là khóa ngoại nếu nó được đặt tên dạng <navigation property name><primary key property name>. Ví dụ: StudentID là khóa ngoại cho property điều hướng Student, vì khóa chính của thực thể là Student ID. Các property khóa ngoại cũng có thể được đặt tên dạng <primary key property name>. Ví dụ: CourseID vì khóa chính của thực thể Course là CourseID.

Thực thể Course

Sơ đồ thực thể khóa học

Tạo Models/Course.cs với đoạn code sau:

using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

Property Enrollments là property điều hướng. Một thực thể Course có thể được liên kết với nhiều thực thể Enrollment.

Property DatabaseGenerated cho phép ứng dụng chỉ định khóa chính thay vì để cơ sở dữ liệu tạo khóa đó.

Bây giờ ta build ứng dụng. Trình biên dịch tạo ra một số cảnh báo về cách các giá trị null được xử lý. Xem vấn đề GitHub nàyCác loại tham chiếu nullable và Hướng dẫn: Thể hiện ý định thiết kế của bạn rõ ràng hơn với các loại tham chiếu nullable và non-nullable để biết thêm thông tin.

Để loại bỏ các cảnh báo khỏi các loại tham chiếu nullable, hãy xóa dòng sau khỏi file ContosoUniversity.csproj:

<Nullable>enable</Nullable>

Công cụ scaffold hiện không hỗ trợ các loại tham chiếu nullable, do đó, các model được sử dụng trong scaffold cũng không thể.

Xóa chú thích kiểu tham chiếu nullable ? khỏi publicstring? RequestId { get; set; } trong Pages/Error.cshtml.cs để dự án được xây dựng mà không có cảnh báo của trình biên dịch.

Scaffold trang Student

Trong phần này, công cụ scaffold ASP.NET Core được sử dụng để tạo:

  • Một lớp EF Core DbContext. Bối cảnh là lớp chính điều phối chức năng Entity Framework cho một mô hình dữ liệu nhất định. Nó bắt nguồn từ lớp Microsoft.EntityFrameworkCore.DbContext.
  • Các trang Razor xử lý các thao tác Create, Read, Update và Delete (CRUD) cho thực thể Student.
  • Tạo thư mục Pages/Students.
  • Trong Solution Explorer, nhấp chuột phải vào thư mục Pages/Students và chọn Add > New Scaffolded Item.
  • Trong hộp thoại Add New Scaffold Item:
    • Trong tab bên trái, chọn Installed > Common > Razor Pages
    • Chọn Razor Pages using Entity Framework (CRUD) > ADD.
  • Trong hộp thoại Add Razor Pages using Entity Framework (CRUD):
    • Trong trình đơn thả xuống Model class, hãy chọn Student (ContosoUniversity.Models).
    • Trong hàng Data context class, hãy chọn dấu + (dấu cộng).
      • Thay đổi tên ngữ cảnh dữ liệu thành kết thúc bằng SchoolContext thay vì ContosoUniversityContext. Tên ngữ cảnh được cập nhật: ContosoUniversity.Data.SchoolContext
      • Chọn Add để hoàn tất việc thêm lớp ngữ cảnh dữ liệu.
      • Chọn Add để kết thúc hộp thoại Add Razor Pages.

Các gói sau được cài đặt tự động:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Nếu bước trên không thành công, hãy xây dựng dự án và thử lại bước scaffold.

Quá trình scaffold:

  • Tạo các trang Razor trong  thư mục Pages/Students:
    • Create.cshtml và Create.cshtml.cs
    • Delete.cshtml và Delete.cshtml.cs
    • Details.cshtml và Details.cshtml.cs
    • Edit.cshtml và Edit.cshtml.cs
    • Index.cshtml và Index.cshtml.cs
  • Tạo Data/SchoolContext.cs.
  • Thêm ngữ cảnh vào dependency injection trong Program.cs.
  • Thêm chuỗi kết nối cơ sở dữ liệu vào appsettings.json.

Chuỗi kết nối cơ sở dữ liệu

Công cụ scaffold tạo chuỗi kết nối trong file appsettings.json.

Chuỗi kết nối chỉ định SQL Server LocalDB:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext-0e9;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

LocalDB là phiên bản nhẹ của SQL Server Express Database Engine và được thiết kế để phát triển ứng dụng, không sử dụng cho production. Theo mặc định, LocalDB tạo các file .mdf  trong thư mục C:/Users/<user>.

Cập nhật lớp database context

Lớp chính điều phối chức năng EF Core cho một model dữ liệu nhất định là lớp database context. Bối cảnh có nguồn gốc từ Microsoft.EntityFrameworkCore.DbContext. Bối cảnh chỉ định những thực thể nào được bao gồm trong model dữ liệu. Trong dự án này, lớp được đặt tên là SchoolContext.

Cập nhật Data/SchoolContext.cs với đoạn code sau:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext (DbContextOptions<SchoolContext> options)
            : base(options)
        {
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

Đoạn mã trên thay đổi từ số ít DbSet<Student> Student sang số nhiều DbSet<Student> Students. Để code Razor Pages khớp với tên DBSet mới, hãy thực hiện thay đổi tổng quan từ _context.Student. thành _context.Students.

Có 8 lần xuất hiện.

Bởi vì một tập hợp thực thể chứa nhiều thực thể, nhiều nhà phát triển muốn đặt tên property DBSet ở dạng số nhiều.

Code được đánh dấu:

  • Tạo property DbSet<TEntity> cho mỗi tập hợp thực thể. Trong thuật ngữ EF Core:
    • Một tập thực thể thường tương ứng với một bảng cơ sở dữ liệu.
    • Một thực thể tương ứng với một hàng trong bảng.
  • Gọi OnModelCreating. Ở đây OnModelCreating:
    • Được gọi khi SchoolContext đã được khởi tạo, nhưng trước khi model bị khóa và được sử dụng để khởi tạo bối cảnh.
    • Là bắt buộc vì sau này trong phần hướng dẫn, thực thể Student sẽ có tham chiếu đến các thực thể khác.

Chúng tôi hy vọng sẽ  khắc phục vấn đề này  trong một bản phát hành trong tương lai.

Program.cs

ASP.NET Core được xây dựng với dependency injection (DI). Các dịch vụ như SchoolContext được đăng ký với DI trong khi khởi động ứng dụng. Các component yêu cầu các dịch vụ này, chẳng hạn như Razor Pages, được cung cấp các dịch vụ này thông qua các tham số hàm tạo. Code hàm tạo nhận một thể hiện database context sẽ được hiển thị sau trong hướng dẫn.

Công cụ scaffold tự động đăng ký lớp ngữ cảnh với bộ chứa DI.

Các dòng được đánh dấu sau đây đã được thêm vào bởi scaffold:

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDbContext<SchoolContext>(options =>
  options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));

Tên của chuỗi kết nối được chuyển vào ngữ cảnh bằng cách gọi một phương thức trên đối tượng DbContextOptions. Để phát triển cục bộ, hệ thống cấu hình ASP.NET Core đọc chuỗi kết nối từ file appsettings.json hoặc appsettings.Development.json.

Thêm bộ lọc ngoại lệ cơ sở dữ liệu

Thêm AddDatabaseDeveloperPageExceptionFilter và UseMigrationsEndPoint như được hiển thị trong đoạn mã sau:

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDbContext<SchoolContext>(options =>
  options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));

builder.Services.AddDatabaseDeveloperPageExceptionFilter();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseMigrationsEndPoint();
}

Thêm gói NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.

Trong Package Manager Console, hãy nhập thông tin sau để thêm gói NuGet:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

Gói NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore cung cấp middleware ASP.NET Core cho các trang lỗi Entity Framework Core. Middleware này giúp phát hiện và chẩn đoán lỗi khi migration Entity Framework Core.

AddDatabaseDeveloperPageExceptionFilter cung cấp thông tin lỗi hữu ích trong môi trường phát triển cho các lỗi di chuyển EF.

Tạo cơ sở dữ liệu

Cập nhật Program.cs để tạo cơ sở dữ liệu nếu nó không tồn tại:

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

builder.Services.AddDbContext<SchoolContext>(options =>
  options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));

builder.Services.AddDatabaseDeveloperPageExceptionFilter();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
else
{
    app.UseDeveloperExceptionPage();
    app.UseMigrationsEndPoint();
}

using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<SchoolContext>();
    context.Database.EnsureCreated();
    // DbInitializer.Initialize(context);
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Phương thức EnsureCreated không thực hiện action nào nếu tồn tại cơ sở dữ liệu cho ngữ cảnh. Nếu không có cơ sở dữ liệu nào tồn tại, nó sẽ tạo cơ sở dữ liệu và lược đồ. EnsureCreated cho phép quy trình công việc sau để xử lý các thay đổi mô hình dữ liệu:

  • Xóa cơ sở dữ liệu. Mọi dữ liệu hiện có đều bị mất.
  • Thay đổi mô hình dữ liệu. Ví dụ: thêm một trường EmailAddress.
  • Chạy ứng dụng.
  • EnsureCreated tạo một cơ sở dữ liệu với lược đồ mới.

Quy trình công việc này hoạt động sớm trong quá trình phát triển khi lược đồ đang phát triển nhanh chóng, miễn là không cần lưu giữ dữ liệu. Tình hình sẽ khác khi dữ liệu đã được nhập vào cơ sở dữ liệu cần được bảo tồn. Trong trường hợp đó, hãy sử dụng migration.

Ở phần sau của loạt bài hướng dẫn, cơ sở dữ liệu được tạo bởi EnsureCreated và migration được sử dụng sẽ bị xóa. Cơ sở dữ liệu được tạo bởi EnsureCreated không thể cập nhật bằng cách sử dụng migration.

Kiểm tra ứng dụng

  • Chạy ứng dụng.
  • Chọn liên kết Student rồi chọn Create New.
  • Kiểm tra các liên kết Edit, Details, và Delete.

Seed cơ sở dữ liệu

Phương thức EnsureCreated tạo một cơ sở dữ liệu trống. Phần này thêm code để điền dữ liệu thử nghiệm vào cơ sở dữ liệu.

Tạo Data/DbInitializer.cs với đoạn code sau:

using ContosoUniversity.Models;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
                new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            context.SaveChanges();

            var courses = new 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}
            };

            context.Courses.AddRange(courses);
            context.SaveChanges();

            var enrollments = new 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},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

Đoạn code có nhiệm vụ kiểm tra xem có sinh viên nào trong cơ sở dữ liệu không. Nếu không có sinh viên nào thì nó sẽ thêm dữ liệu kiểm tra vào cơ sở dữ liệu. Nó tạo dữ liệu thử nghiệm trong mảng thay vì collection List<T> để tối ưu hóa hiệu suất.

  • Trong Program.cs ta bỏ // ở dòng DbInitializer.Initialize:
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<SchoolContext>();
    context.Database.EnsureCreated();
    DbInitializer.Initialize(context);
}
  • Dừng ứng dụng nếu nó đang chạy và chạy lệnh sau trong Package Manager Console (PMC):
Drop-Database -Confirm
  • Chọn trả lời Y để xóa cơ sở dữ liệu.
  • Khởi động lại ứng dụng.
  • Chọn trang Student để xem dữ liệu đã tạo.

Xem cơ sở dữ liệu

  • Mở SQL Server Object Explorer (SSOX) từ menu View trong Visual Studio.
  • Trong SSOX, chọn (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. Tên cơ sở dữ liệu được tạo từ tên ngữ cảnh được cung cấp trước đó cùng với dấu gạch ngang và GUID.
  • Mở rộng nút Tables.
  • Bấm chuột phải vào bảng Student và bấm View Data để xem các cột được tạo và các hàng được chèn vào bảng.
  • Bấm chuột phải vào bảng Student và bấm View Code để xem cách model Student ánh xạ tới lược đồ bảng Student.

Các phương thức EF không đồng bộ trong ứng dụng web ASP.NET Core

Lập trình không đồng bộ là chế độ mặc định của ASP.NET Core và EF Core.

Một máy chủ web có sẵn một số thread hạn chế và trong các tình huống tải cao, tất cả các thread có sẵn đều có thể được sử dụng. Khi điều đó xảy ra, máy chủ không thể xử lý các yêu cầu mới cho đến khi các luồng được giải phóng. Với code đồng bộ, nhiều thread có thể bị ràng buộc trong khi chúng không hoạt động vì chúng đang chờ I/O hoàn tất. Với code không đồng bộ, khi một tiến trình đang đợi I/O hoàn tất, luồng của nó sẽ được giải phóng để máy chủ sử dụng để xử lý các yêu cầu khác. Do đó, code không đồng bộ cho phép sử dụng tài nguyên máy chủ hiệu quả hơn và máy chủ có thể xử lý nhiều lưu lượng truy cập hơn mà không bị chậm trễ.

Code không đồng bộ giới thiệu một lượng nhỏ chi phí trong thời gian chạy. Đối với các tình huống lưu lượng truy cập thấp, hiệu suất đạt được là không đáng kể, trong khi đối với các tình huống lưu lượng truy cập cao, khả năng cải thiện hiệu suất là đáng kể.

Trong đoạn mã sau, từ khóa asyncTask sẽ trả về giá trị, từ khóa await và phương thức ToListAsync làm cho code thực thi không đồng bộ.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • Từ  async khóa cho trình biên dịch biết:
    • Tạo các lời gọi lại cho các phần của nội dung phương thức.
    • Tạo đối tượng Tác vụ được trả về.
  • Kiểu trả về Task đại diện cho công việc đang diễn ra.
  • Từ khóa await khiến trình biên dịch chia phương thức thành hai phần. Phần đầu tiên kết thúc với hoạt động bắt đầu không đồng bộ. Phần thứ hai được đưa vào một phương thức gọi lại được gọi khi thao tác hoàn tất.
  • ToListAsync là thể hiện không đồng bộ của phương thức mở rộng ToList.

Một số điều cần lưu ý khi viết code không đồng bộ sử dụng EF Core:

  • Chỉ các câu lệnh gây ra các truy vấn hoặc lệnh được gửi đến cơ sở dữ liệu được thực thi không đồng bộ. Điều đó bao gồm ToListAsyncSingleOrDefaultAsyncFirstOrDefaultAsync và SaveChangesAsync. Nó không bao gồm các câu lệnh chỉ thay đổi một IQueryable, chẳng hạn như var students = context.Students.Where(s => s.LastName == "Davolio").
  • Bối cảnh EF Core không phải là luồng an toàn: đừng cố thực hiện song song nhiều thao tác.
  • Để tận dụng các lợi ích hiệu suất của code không đồng bộ, hãy xác minh rằng các gói thư viện (chẳng hạn như để phân trang) sử dụng không đồng bộ nếu chúng gọi các phương thức EF Core gửi truy vấn đến cơ sở dữ liệu.

Để biết thêm thông tin về lập trình không đồng bộ trong .NET, hãy xem Tổng quan về Async và Lập trình không đồng bộ với async và await.

Cảnh báo

Việc triển khai không đồng bộ của Microsoft.Data.SqlClient có một số vấn đề đã biết (#593#601 và các vấn đề khác). Nếu bạn đang gặp sự cố hiệu suất không mong muốn, thay vào đó, hãy thử sử dụng thực thi lệnh đồng bộ hóa, đặc biệt là khi xử lý các giá trị nhị phân hoặc văn bản lớn.

Cân nhắc hiệu suất

Nói chung, một trang web không nên tải một số hàng tùy ý. Một truy vấn nên sử dụng phân trang hoặc một cách tiếp cận giới hạn. Ví dụ: truy vấn trước đó có thể sử dụng Take để giới hạn các hàng được trả về:

public async Task OnGetAsync()
{
    Student = await _context.Students.Take(10).ToListAsync();
}

Việc liệt kê một bảng lớn trong dạng xem có thể trả về phản hồi HTTP 200 được xây dựng một phần nếu ngoại lệ cơ sở dữ liệu xảy ra trong quá trình liệt kê.

Phân trang được đề cập sau trong hướng dẫn.

Để biết thêm thông tin, hãy xem Cân nhắc hiệu suất (EF).

Bước tiếp theo

Sử dụng SQLite để phát triển, SQL Server để tạo bản production.

Hướng dẫn tiếp theo

Nguồn: learn.microsoft.com
» Tiếp: Razor Pages với Entity Framework Core trong ASP.NET Core - CRUD - Hướng dẫn 2/8
« Trước: ASP.NET Quiz
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!