ASP.NET Core: Bắt đầu với EF Core trong ứng dụng web ASP.NET MVC


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

Trong bài viết này

  1. Điều kiện tiên quyết
  2. Công cụ cơ sở dữ liệu
  3. Giải quyết vấn đề và khắc phục sự cố
  4. Ứng dụng web của Đại học Contoso
  5. Tạo ứng dụng web
  6. Thiết lập kiểu trang web
  7. Gói NuGet lõi của EF
  8. Tạo mô hình dữ liệu
  9. Tạo ngữ cảnh cơ sở dữ liệu
  10. Đăng ký SchoolContext
  11. Khởi tạo DB với dữ liệu thử nghiệm
  12. Tạo controller và view
  13. Xem cơ sở dữ liệu
  14. Quy ước
  15. Mã không đồng bộ
  16. Giới hạn các thực thể được tìm nạp
  17. Ghi nhật ký SQL của EF Core

Hướng dẫn này dạy ASP.NET Core MVC và Entity Framework Core với controller và view. Razor Pages là một mô hình lập trình thay thế. Để phát triển mới, chúng tôi khuyên dùng Razor Pages trên MVC với controller và view. Xem phiên bản Razor Pages của hướng dẫn này. Mỗi hướng dẫn bao gồm một số tài liệu mà hướng dẫn kia không có:

Một số điều mà hướng dẫn MVC này có mà hướng dẫn Razor Pages không có:

  • Thực hiện kế thừa trong mô hình dữ liệu
  • Thực hiện các truy vấn SQL thô
  • Sử dụng LINQ động để đơn giản hóa code

Một số điều mà hướng dẫn về Razor Pages có mà hướng dẫn này không có:

  • Sử dụng phương thức Select để tải dữ liệu liên quan
  • Các phương pháp hay nhất cho EF.

Ứng dụng web mẫu của Đại học Contoso trình bày cách tạo ứng dụng web ASP.NET Core MVC bằng cách sử dụng Entity Framework (EF) Core và Visual Studio.

Ứng dụng mẫu là một trang web hư cấu về Đại học Contoso. Nó bao gồm các chức năng như tuyển sinh, tạo khóa học và bài tập của người hướng dẫn. Đây là bài đầu tiên trong loạt bài hướng dẫn giải thích cách xây dựng ứng dụng mẫu của Đại học Contoso.

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

Hướng dẫn này chưa được cập nhật cho ASP.NET Core 6 trở lên. Hướng dẫn của hướng dẫn sẽ không hoạt động chính xác nếu bạn tạo một dự án nhắm mục tiêu ASP.NET Core 6 hoặc 7. Ví dụ: các mẫu web ASP.NET Core 6 và 7 sử dụng mô hình lưu trữ tối thiểu, hợp nhất Startup.cs và Program.cs thành một file duy nhất Program.cs.

Một điểm khác biệt khác được giới thiệu trong .NET 6 là tính năng NRT (loại tham chiếu có thể rỗng). Các mẫu dự án kích hoạt tính năng này theo mặc định. Sự cố có thể xảy ra khi EF coi một property là bắt buộc trong .NET 6, property này là null trong .NET 5. Ví dụ: trang Create Student sẽ âm thầm thất bại trừ khi property Enrollments được đặt thành null hoặc thẻ trợ giúp asp-validation-summary được thay đổi từ ModelOnly thành All.

Chúng tôi khuyên bạn nên cài đặt và sử dụng .NET 5 SDK cho hướng dẫn này. Cho đến khi hướng dẫn này được cập nhật, hãy xem Trang Razor với Entity Framework Core trong ASP.NET Core - Hướng dẫn 1/8 về cách sử dụng Entity Framework với ASP.NET Core 6 trở lên.

Công cụ cơ sở dữ liệu

Hướng dẫn của Visual Studio sử dụng SQL Server LocalDB, một phiên bản SQL Server Express chỉ chạy trên Windows.

Giải quyết vấn đề và khắc phục sự cố

Nếu gặp phải một vấn đề không thể giải quyết, bạn thường có thể tìm ra giải pháp bằng cách so sánh code của mình với dự án đã hoàn thành. Để biết danh sách các lỗi phổ biến và cách giải quyết chúng, hãy xem phần Khắc phục sự cố của hướng dẫn cuối cùng trong loạt bài. Nếu bạn không tìm thấy thứ mình cần ở đó, bạn có thể đăng câu hỏi lên StackOverflow.com cho ASP.NET Core hoặc EF Core.

Mẹo

Đây là một chuỗi gồm 10 hướng dẫn, mỗi hướng dẫn được xây dựng dựa trên những gì đã được thực hiện trong các hướng dẫn trước đó. Hãy cân nhắc việc lưu một bản sao của dự án sau mỗi lần hoàn thành hướng dẫn thành công. Sau đó, nếu gặp vấn đề, bạn có thể bắt đầu lại từ hướng dẫn trước thay vì quay lại phần đầu của toàn bộ loạt bài.

Ứng dụng web của Đại học Contoso

Ứng dụng được xây dựng trong những hướng dẫn này là một trang web cơ bản của một trường đại học.

Người dùng có thể xem và cập nhật thông tin 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 trong ứng dụng:

Trang chỉ mục sinh viên

Trang chỉnh sửa sinh viên

Tạo ứng dụng web

  1. Khởi động Visual Studio và chọn Create a new project.
  2. Trong hộp thoại Create a new project, chọn ASP.NET Core Web Application > Next.
  3. Trong hộp thoại Configure your new project, hãy nhập ContosoUniversity cho Project name. Điều quan trọng là phải sử dụng tên chính xác này bao gồm cả cách viết hoa để mỗi namespace đều khớp khi sao chép code.
  4. Chọn Create.
  5. Trong hộp thoại Create a new ASP.NET Core web application, chọn:
    1. .NET Core và ASP.NET Core 5.0 trong danh sách thả xuống.
    2. ASP.NET Core Web App (Model-View-Controller).
    3. Create Hộp thoại Dự án ASP.NET Core mới

Thiết lập kiểu trang web

Một số thay đổi cơ bản về thiết lập menu, bố cục và trang chủ của trang.

Mở Views/Shared/_Layout.cshtml và thực hiện các thay đổi sau:

  • Thay đổi mỗi lần xuất hiện của ContosoUniversity thành Contoso University. Có ba lần xuất hiện.
  • Thêm các mục menu cho AboutStudentsCoursesConstructors và Departments và xóa mục menu Privacy.

Những thay đổi trên được đánh dấu trong đoạn code sau:

<!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" />
</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-controller="Home" asp-action="Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-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-controller="Home" asp-action="Index">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="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; 2020 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="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>

Trong Views/Home/Index.cshtml, thay thế nội dung của file bằng đánh dấu sau:

@{
    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 that
            demonstrates how to use Entity Framework Core in an
            ASP.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 &raquo;</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/dotnet/AspNetCore.Docs/tree/main/aspnetcore/data/ef-mvc/intro/samples/5cu-final">See project source code &raquo;</a></p>
    </div>
</div>

Nhấn CTRL+F5 để chạy dự án hoặc chọn Debug > Start Without Debugging từ menu. Trang chủ được hiển thị với các tab dành cho các trang được tạo trong hướng dẫn này.

Trang chủ Đại học Contoso

Gói NuGet lõi của EF

Hướng dẫn này sử dụng SQL Server và gói nhà cung cấp (provider) là Microsoft.EntityFrameworkCore.SqlServer.

Gói EF SQL Server và các phần phụ thuộc của nó là Microsoft.EntityFrameworkCore và Microsoft.EntityFrameworkCore.Relational cung cấp hỗ trợ thời gian chạy cho EF.

Thêm gói NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore. Trong Package Manager Console (PMC), nhập các lệnh sau để thêm các gói NuGet:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer

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

Để biết thông tin về các nhà cung cấp cơ sở dữ liệu khác có sẵn cho EF Core, hãy xem Nhà cung cấp cơ sở dữ liệu.

Tạo mô hình dữ liệu

Các lớp thực thể sau được tạo cho ứng dụng này:

Sơ đồ mô hình dữ liệu khóa học-Ghi danh-Sinh viên

Các thực thể trên có các mối quan hệ sau:

  • Mối quan hệ một-nhiều giữa các thực thể Student và Enrollment. Một sinh viên có thể được ghi danh vào nhiều khóa học.
  • Mối quan hệ một-nhiều giữa các thực thể Course và Enrollment. Một khóa học có thể có nhiều sinh viên đăng ký tham gia.

Trong các phần sau, mỗi lớp sẽ được tạo cho mỗi thực thể.

Thực thể Student

Sơ đồ thực thể Student

Trong thư mục Models, tạo lớp Student với mã sau:

using System;
using System.Collections.Generic;

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 là cột khóa chính (PK) của bảng cơ sở dữ liệu tương ứng với lớp này. Theo mặc định, EF diễn giải property được đặt tên ID hoặc classnameID làm khóa chính. Ví dụ: PK có thể được đặt tên StudentID thay vì ID.

Property Enrollments là property điều hướng. Property điều hướng chứa các thực thể khác có liên quan đến thực thể này. Property Enrollments của thực thể Student:

  • Chứa tất cả các thực thể Enrollment có liên quan đến thực thể Student đó.
  • Nếu một hàng Student cụ thể trong cơ sở dữ liệu có hai hàng Enrollment liên quan:
    • Thuộc tính điều hướng Enrollments của thực thể Student đó chứa hai thực thể Enrollment đó.

Các hàng Enrollment chứa giá trị PK của sinh viên trong cột khóa ngoại (FK) StudentID.

Nếu thuộc tính điều hướng có thể chứa nhiều thực thể:

  • Kiểu phải là một danh sách, chẳng hạn như ICollection<T>List<T> hoặc HashSet<T>.
  • Các thực thể có thể được thêm, xóacập nhật.

Mối quan hệ điều hướng nhiều-nhiềumột-nhiều có thể chứa nhiều thực thể. Khi ICollection<T> được sử dụng, EF sẽ tạo một collection HashSet<T> theo mặc định.

Thực thể Enrollment

Sơ đồ thực thể Enrollment

Trong thư mục Models, tạo lớp Enrollment với mã sau:

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; }
        public Grade? Grade { get; set; }

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

Property EnrollmentID là PK. Thực thể này sử dụng mẫu classnameID thay vì sử dụng ID. Thực thể Student đã sử dụng mẫu ID này. Một số nhà phát triển thích sử dụng một mẫu trong toàn bộ mô hình dữ liệu. Trong hướng dẫn này, biến thể minh họa rằng có thể sử dụng một trong hai mẫu. Hướng dẫn sau sẽ chỉ ra cách sử dụng ID không có tên lớp giúp triển khai tính kế thừa trong mô hình dữ liệu dễ dàng hơn.

Property Grade là một enum. Phần ? sau Grade khai báo kiểu chỉ ra rằng property Grade là nullable. Điểm là null sẽ khác so với điểm 0. null có nghĩa là điểm chưa được biết hoặc chưa được chỉ định.

Property StudentID là khóa ngoại (FK) 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, do đó property chỉ có thể chứa một thực thể Student duy nhất. Điều này khác với property điều hướng Student.Enrollments, có thể chứa nhiều thực thể Enrollment.

Property CourseID là FK 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ực thể Course.

Entity Framework diễn giải một property là property FK nếu property đó được đặt tên là <tên property điều hướng><tên property khóa chính>. Ví dụ: StudentID đối với thuộc tính điều hướng Student vì PK của thực thể Student là ID. Thuộc tính FK cũng có thể được đặt tên là <tên property khóa chính>. Ví dụ: CourseID vì PK của thực thể Course là CourseID.

Thực thể Course

Sơ đồ thực thể Course

Trong thư mục Models, tạo lớp Course với mã sau:

using System.Collections.Generic;
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à thuộc tính điều hướng. Một thực thể Course có thể liên kết với nhiều thực thể Enrollment.

Attribute DatabaseGenerated sẽ được giải thích trong phần hướng dẫn sau. Attribute này cho phép nhập PK cho khóa học thay vì để cơ sở dữ liệu tạo ra nó.

Tạo database context

Lớp chính điều phối chức năng EF cho một mô hình dữ liệu nhất định là lớp ngữ cảnh cơ sở dữ liệu DbContext. Lớp này được tạo bằng cách xuất phát từ lớp Microsoft.EntityFrameworkCore.DbContext. Lớp dẫn xuất DbContext chỉ định các thực thể nào được đưa vào mô hình dữ liệu. Một số hành vi của EF có thể được tùy chỉnh. Trong dự án này, lớp được đặt tên là SchoolContext.

Trong thư mục dự án, tạo một thư mục có tên Data.

Trong thư mục Data, tạo một lớp SchoolContext có mã sau:

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

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

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

Đoạn mã trên tạo mỗi property DbSet cho mỗi tập thực thể. Trong thuật ngữ EF:

  • 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.

Các câu lệnh DbSet<Enrollment> và DbSet<Course> có thể được bỏ qua và nó sẽ hoạt động tương tự. EF sẽ bao gồm chúng một cách ngầm định vì:

  • Thực thể Student tham chiếu đến thực thể Enrollment.
  • Thực thể Enrollment tham chiếu đến thực thể Course.

Khi cơ sở dữ liệu được tạo, EF sẽ tạo các bảng có tên giống với tên property DbSet. Tên property cho collection thường ở dạng số nhiều. Ví dụ, Students thay vì Student. Các nhà phát triển không đồng ý về việc tên bảng có nên ở dạng số nhiều hay không. Đối với những hướng dẫn này, hành vi mặc định được ghi đè bằng cách chỉ định tên bảng số ít trong file DbContext. Để làm điều đó, hãy thêm mã được đánh dấu sau đây sau property DbSet cuối cùng.

using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;

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

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

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

Đăng ký SchoolContext

ASP.NET Core bao gồm tính năng chèn phụ thuộc. Các dịch vụ, chẳng hạn như ngữ cảnh cơ sở dữ liệu EF, được đăng ký bằng tính năng chèn phụ thuộc trong quá trình khởi động ứng dụng. Các thành phần yêu cầu các dịch vụ này, chẳng hạn như controller MVC, được cung cấp các dịch vụ này thông qua các tham số của hàm tạo. Mã hàm tạo controller lấy phiên bản ngữ cảnh sẽ được hiển thị ở phần sau của hướng dẫn này.

Để đăng ký SchoolContext làm dịch vụ, hãy mở Startup.cs và thêm các dòng được đánh dấu vào phương thức ConfigureServices.

using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ContosoUniversity
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<SchoolContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddControllersWithViews();
        }

Tên của chuỗi kết nối được truyền vào ngữ cảnh bằng cách gọi một phương thức trên một đối tượng DbContextOptionsBuilder. Để 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.

Mở file appsettings.json và thêm chuỗi kết nối như trong phần đánh dấu sau:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

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

Thêm AddDatabaseDeveloperPageExceptionFilter vào ConfigureServices như trong đoạn mã sau:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<SchoolContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddDatabaseDeveloperPageExceptionFilter();

    services.AddControllersWithViews();
}

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

SQL Server Express LocalDB

Chuỗi kết nối chỉ định SQL Server LocalDB. LocalDB là phiên bản nhẹ của Công cụ cơ sở dữ liệu SQL Server Express và được thiết kế để phát triển ứng dụng chứ không phải để sử dụng trong production. LocalDB khởi động theo yêu cầu và chạy ở chế độ người dùng, do đó không cần cấu hình phức tạp. Theo mặc định, LocalDB tạo các tệp DB .mdf trong thư mục C:/Users/<user>.

Khởi tạo DB với dữ liệu thử nghiệm

EF tạo một cơ sở dữ liệu trống. Trong phần này, một phương thức được thêm vào sẽ được gọi sau khi cơ sở dữ liệu được tạo để điền dữ liệu thử nghiệm vào đó.

Phương thức EnsureCreated được sử dụng để tự động tạo cơ sở dữ liệu. Trong hướng dẫn sau, bạn sẽ thấy cách xử lý các thay đổi model bằng cách sử dụng Code First Migrations để thay đổi lược đồ cơ sở dữ liệu thay vì bỏ và tạo lại cơ sở dữ liệu.

Trong thư mục Data, tạo một lớp mới có tên DbInitializer bằng mã sau:

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // 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("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            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}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            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},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}

Đoạn mã trên kiểm tra xem cơ sở dữ liệu có tồn tại không:

  • Nếu không tìm thấy cơ sở dữ liệu:
    • Nó được tạo và tải với dữ liệu thử nghiệm. Nó tải dữ liệu thử nghiệm vào mảng thay vì collection List<T> để tối ưu hóa hiệu suất.
  • Nếu cơ sở dữ liệu được tìm thấy, nó sẽ không có hành động nào.

Cập nhật Program.cs với đoạn mã sau:

using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Program.cs thực hiện như sau khi khởi động ứng dụng:

  • Lấy một phiên bản ngữ cảnh cơ sở dữ liệu từ vùng chứa chèn phụ thuộc.
  • Gọi phương thức DbInitializer.Initialize.
  • Loại bỏ ngử cảnh khi phương thức Initialize hoàn thành như trong đoạn mã sau:
public static void Main(string[] args)
{
     var host = CreateWebHostBuilder(args).Build();

    using (var scope = host.Services.CreateScope())
    {
        var services = scope.ServiceProvider;
        try
        {
            var context = services.GetRequiredService<SchoolContext>();
            DbInitializer.Initialize(context);
        }
        catch (Exception ex)
        {
            var logger = services.GetRequiredService<ILogger<Program>>();
            logger.LogError(ex, "An error occurred while seeding the database.");
        }
    }

    host.Run();
}

Lần đầu tiên ứng dụng được chạy, cơ sở dữ liệu sẽ được tạo và tải dữ liệu thử nghiệm. Bất cứ khi nào mô hình dữ liệu thay đổi:

  • Xóa cơ sở dữ liệu.
  • Cập nhật phương thức hạt giống (seed) và bắt đầu lại từ đầu với cơ sở dữ liệu mới.

Trong các hướng dẫn sau, cơ sở dữ liệu sẽ được sửa đổi khi mô hình dữ liệu thay đổi mà không cần xóa và tạo lại nó. Không có dữ liệu nào bị mất khi mô hình dữ liệu thay đổi.

Tạo controller và view

Sử dụng công cụ scaffold trong Visual Studio để thêm controller MVC và các view sẽ sử dụng EF để truy vấn và lưu dữ liệu.

Việc tự động tạo các action method và view CRUD được gọi là scaffold.

  • Trong Solution Explorer, nhấp chuột phải vào thư mục Controllers và chọn Add > New Scaffolded Item.
  • Trong hộp thoại Add Scaffold:
    • Chọn MVC controller with views, using Entity Framework.
    • Nhấp vào Add. Hộp thoại Add MVC Controller with views, using Entity Framework xuất hiện: Scaffold Student
    • Trong Model class, chọn Student.
    • Trong Data context class, chọn SchoolContext.
    • Chấp nhận tên mặc định là StudentsController.
    • Nhấp vào Add.

Công cụ scaffold Visual Studio tạo một file StudentsController.cs và một tập hợp các view (file *.cshtm) hoạt động với controller.

Lưu ý rằng controller lấy SchoolContext làm tham số hàm tạo.

namespace ContosoUniversity.Controllers
{
    public class StudentsController : Controller
    {
        private readonly SchoolContext _context;

        public StudentsController(SchoolContext context)
        {
            _context = context;
        }

Việc chèn phụ thuộc ASP.NET Core đảm nhiệm việc chuyển một phiên bản của SchoolContext vào controller. Bạn đã cấu hình nó trong lớp Startup.

Controller chứa một phương thức hành động Index, hiển thị tất cả sinh viên trong cơ sở dữ liệu. Phương thức này lấy danh sách sinh viên từ tập thực thể Student bằng cách đọc property Students của phiên bản ngữ cảnh cơ sở dữ liệu:

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}

Các phần tử lập trình không đồng bộ trong mã này sẽ được giải thích sau trong hướng dẫn.

View Views/Students/Index.cshtml hiển thị danh sách này trong một bảng:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
                <th>
                    @Html.DisplayNameFor(model => model.LastName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.FirstMidName)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.EnrollmentDate)
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.ID">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Nhấn CTRL+F5 để chạy dự án hoặc chọn Debug > Start Without Debugging từ menu.

Bấm vào tab Student để xem dữ liệu kiểm tra mà phương thức DbInitializer.Initialize đã chèn. Tùy thuộc vào mức độ hẹp của cửa sổ trình duyệt, bạn sẽ thấy link tab Students ở đầu trang hoặc bạn sẽ phải nhấp vào biểu tượng điều hướng ở góc trên bên phải để xem liên kết.

Trang chủ Đại học Contoso thu hẹp

Trang chủ sinh viên

Xem cơ sở dữ liệu

Khi ứng dụng được khởi động, phương thức DbInitializer.Initialize sẽ gọi EnsureCreated. EF thấy rằng không có cơ sở dữ liệu:

  • Vì vậy, nó đã tạo ra một cơ sở dữ liệu.
  • Mã phương thức Initialize đã điền dữ liệu vào cơ sở dữ liệu.

Sử dụng SQL Server Object Explorer (SSOX) để xem cơ sở dữ liệu trong Visual Studio:

  • Chọn SQL Server Object Explorer từ menu View trong Visual Studio.
  • Trong SSOX, chọn (localdb)\MSSQLLocalDB > Databases.
  • Chọn ContosoUniversity1, đầu vào tên cơ sở dữ liệu có trong chuỗi kết nối trong file appsettings.json.
  • Mở rộng nút Tables để xem các bảng trong cơ sở dữ liệu.

Các bảng trong SSOX

Bấm chuột phải vào bảng Student và bấm View Data để xem dữ liệu trong bảng.

Bảng Students trong SSOX

Các file cơ sở dữ liệu *.mdf và *.ldf nằm trong thư mục C:\Users\<username>.

Vì EnsureCreated được gọi trong phương thức khởi tạo chạy khi khởi động ứng dụng nên bạn có thể:

  • Thực hiện một sự thay đổi cho lớp Student.
  • Xóa cơ sở dữ liệu.
  • Dừng lại, sau đó khởi động ứng dụng. Cơ sở dữ liệu được tự động tạo lại để phù hợp với thay đổi.

Ví dụ: nếu một property EmailAddress được thêm vào lớp Student, thì một cột mới là EmailAddress trong bảng được tạo lại. View sẽ không hiển thị property mới EmailAddress.

Quy ước

Số lượng mã được viết để EF tạo một cơ sở dữ liệu hoàn chỉnh là tối thiểu do việc sử dụng các quy ước mà EF sử dụng:

  • Tên của các property DbSet được sử dụng làm tên bảng. Đối với các thực thể không được tham chiếu bởi property DbSet, thì tên lớp thực thể được sử dụng làm tên bảng.
  • Tên property thực thể được sử dụng cho tên cột.
  • Property thực thể được đặt tên là ID hoặc classnameID được công nhận là property PK.
  • Một property được hiểu là property FK nếu property đó được đặt tên là <tên property điều hướng tên><property PK>. Ví dụ: StudentID đối với property điều hướng Student vì PK của thực thể Student là ID. Property FK cũng có thể được đặt tên là <tên property khóa chính>. Ví dụ: EnrollmentID vì PK của thực thể Enrollment là EnrollmentID.

Hành vi thông thường có thể được ghi đè. Ví dụ: tên bảng có thể được chỉ định rõ ràng, như được trình bày ở trên trong hướng dẫn này. Tên cột và bất kỳ property nào có thể được đặt làm PK hoặc FK.

Mã không đồng bộ

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

Một máy chủ web có số lượng luồng sẵn có hạn chế và trong tình huống tải cao, tất cả các luồng có sẵn 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 mã đồng bộ, nhiều luồng có thể bị ràng buộc trong khi chúng không thực sự thực hiện bất kỳ công việc nào vì chúng đang chờ I/O hoàn tất. Với mã không đồng bộ, khi một tiến trình đang chờ 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 đó, mã 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 kích hoạt để xử lý nhiều lưu lượng truy cập hơn mà không bị chậm trễ.

Mã không đồng bộ tạo ra một lượng nhỏ chi phí trong thời gian chạy, nhưng đối với các tình huống lưu lượng truy cập thấp thì 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 tiềm năng là đáng kể.

Trong đoạn mã sau, asyncTask<T>, await và ToListAsync làm cho mã thực thi không đồng bộ.

public async Task<IActionResult> Index()
{
    return View(await _context.Students.ToListAsync());
}
  • Từ khóa async yêu cầu trình biên dịch tạo các lệnh gọi lại cho các phần của thân phương thức và tự động tạo đối tượng Task<IActionResult> mà được trả về.
  • Kiểu trả về Task<IActionResult> thể hiện công việc đang diễn ra với kết quả là kiểu IActionResult.
  • 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 thao tác được 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à phiên bả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 mã không đồng bộ sử dụng EF:

  • Chỉ những câu lệnh khiến truy vấn hoặc lệnh được gửi tới cơ sở dữ liệu mới được thực thi không đồng bộ. Điều đó bao gồm, ví dụ, ToListAsyncSingleOrDefaultAsync và SaveChangesAsync. Ví dụ: nó không bao gồm các câu lệnh chỉ thay đổi một tệp IQueryable, chẳng hạn như var students = context.Students.Where(s => s.LastName == "Davolio").
  • Ngữ cảnh EF không an toàn cho luồng: đừng cố thực hiện nhiều thao tác song song. Khi bạn gọi bất kỳ phương thức EF không đồng bộ nào, hãy luôn sử dụng từ khóa await.
  • Để tận dụng lợi ích hiệu suất của mã async, hãy đảm bảo rằng mọi gói thư viện được sử dụng cũng sử dụng async nếu chúng gọi bất kỳ phương thức EF nào khiến truy vấn được gửi tới 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.

Giới hạn các thực thể được tìm nạp

Xem Các cân nhắc về hiệu suất để biết thông tin về việc giới hạn số lượng thực thể được trả về từ một truy vấn.

Ghi nhật ký SQL của Entity Framework Core

Cấu hình ghi nhật ký thường được cung cấp bởi phần Logging của file appsettings.{Environment}.json. Để ghi lại các câu lệnh SQL, hãy thêm "Microsoft.EntityFrameworkCore.Database.Command": "Information" vào file appsettings.Development.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
     ,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "AllowedHosts": "*"
}

Với JSON trên, các câu lệnh SQL được hiển thị trên dòng lệnh và trong cửa sổ đầu ra của Visual Studio.

Để biết thêm thông tin, hãy xem Đăng nhập .NET Core và ASP.NET Core và vấn đề GitHub.

Chuyển sang hướng dẫn tiếp theo để tìm hiểu cách thực hiện các thao tác CRUD (tạo, đọc, cập nhật, xóa) cơ bản.

Triển khai chức năng CRUD cơ bản

Nguồn: learn.microsoft.com
» Tiếp: Triển khai chức năng CRUD - ASP.NET MVC với EF Core
« Trước: Khắc phục sự cố gRPC trên .NET
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 !!!