Nguon: Microsoft Learn · .NET 8.0

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

Nguồn: Razor Pages with Entity Framework Core in ASP.NET Core - Tutorial 1 of 8

Đây là series hướng dẫn toàn diện để xây dựng ứng dụng web sử dụng ASP.NET Core Razor Pages và Entity Framework (EF) Core (khung thực thể). Hướng dẫn xây dựng trang web đại học hư cấu Contoso University với chức năng nhập học sinh, tạo khóa học và phân công giảng viên.

Tổng quan

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

Cho Visual Studio

Cho Visual Studio Code

Kiến trúc Data Model (Mô hình dữ liệu)

Ứng dụng sử dụng ba entity (thực thể) chính với các mối quan hệ:

code
Student ←→ Enrollment ←→ Course
(1 đến Nhiều)      (Nhiều đến Nhiều)

Student Entity

csharp
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; }
    }
}

Điểm chú ý:

Enrollment Entity

csharp
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; }
    }
}

Điểm chú ý:

Course Entity

csharp
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; }
    }
}

Điểm chú ý:

Scaffolding (Tạo tự động) Student Pages

Quá trình scaffolding tạo ra:

Cho Visual Studio

  1. Tạo thư mục Pages/Students
  2. Click chuột phải vào thư mục → Add → New Scaffolded Item
  3. Chọn "Razor Pages using Entity Framework (CRUD)"
  4. Cấu hình:
  5. Model class: Student
  6. Data context: ContosoUniversity.Data.SchoolContext

Cho Visual Studio Code

Cài đặt các package cần thiết:

dotnetcli
dotnet add package Microsoft.EntityFrameworkCore.SQLite
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Tools
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

Cài đặt scaffolding tool:

dotnetcli
dotnet tool install --global dotnet-aspnet-codegenerator

Chạy scaffolding (Windows):

dotnetcli
dotnet aspnet-codegenerator razorpage -m Student -dc ContosoUniversity.Data.SchoolContext -udl -outDir Pages\Students --referenceScriptLibraries -dbProvider sqlite

Cấu hình Cơ sở dữ liệu

Thiết lập SchoolContext

csharp
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");
        }
    }
}

Connection Strings (Chuỗi kết nối)

Cho Visual Studio (SQL Server LocalDB):

json
{
  "ConnectionStrings": {
    "SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext-0e9;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

Cho Visual Studio Code (SQLite):

json
{
  "ConnectionStrings": {
    "SchoolContextSQLite": "Data Source=CU.db"
  }
}

Cấu hình Program.cs

csharp
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

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

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();

Seed (Khởi tạo) Cơ sở dữ liệu

Tạo file Data/DbInitializer.cs:

csharp
using ContosoUniversity.Models;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            if (context.Students.Any())
            {
                return;   // DB đã được seed
            }

            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();
        }
    }
}

Lập trình bất đồng bộ (Asynchronous Programming)

EF Core và ASP.NET Core sử dụng async theo mặc định để sử dụng tài nguyên hiệu quả hơn:

csharp
public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}

Điểm chú ý:

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

  1. Pagination (Phân trang): Giới hạn các hàng trả về bằng Take():
csharp
public async Task OnGetAsync()
{
    Students = await _context.Students.Take(10).ToListAsync();
}
  1. Tránh Enumeration lớn: Các bảng lớn nên được phân trang để tránh các HTTP response bị cắt ngang khi xảy ra lỗi
  2. MaxModelBindingCollectionSize: Mặc định là 1024, có thể cấu hình qua settings
  3. Paging: Sẽ được đề cập trong các hướng dẫn sau

Xem cơ sở dữ liệu

Visual Studio: Sử dụng SQL Server Object Explorer (SSOX)

Visual Studio Code: Sử dụng DB Browser for SQLite

Layout của trang

File Pages/Shared/_Layout.cshtml bao gồm:

Quá trình scaffolding tạo ra:

Các bước tiếp theo

Hướng dẫn này bao gồm nền tảng. Các hướng dẫn tiếp theo trong series đề cập: