C# cơ bản .NET Core

Làm việc với Database với Entity Framework Core trong ASP.NET Core

Trong phần này sẽ tìm hiểu cách dùng Entity Framework Core trong ứng dụng Web - ASP.NET Core, để tương tác với các cơ sở dữ liệu. Trước tiên tạo ra dự án mẫu để thực hành, trong dự án sẽ thêm Model biểu diễn dữ liệu Article là các bài viết.

Tạo thư mục dự án razor08.efcore thực hiện lệnh sau để tạo dự án Razor Pages

dotnet new webapp

Để làm việc với EF, hãy thêm các công cụ vào dotnet cũng như tích hợp các Package cần thiết, thực hiện các lệnh sau:

dotnet tool install --global dotnet-ef
dotnet tool install --global dotnet-aspnet-codegenerator
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.SqlServer

Page Microsoft.EntityFrameworkCore.DesignMicrosoft.EntityFrameworkCore.SqlServer cần phải có để thực hiện thao tác scaffolding (phát sinh tự động code CRUD (create, read, update, delete) theo model.

Nếu muốn kết nối đến SQLite thì thực hiện lệnh

dotnet add package Microsoft.EntityFrameworkCore.SQLite

Nếu muốn kết nối đến MySQL thì thực hiện lệnh để thêm package tương ứng

dotnet add package MySql.Data.EntityFramework

Tạo Model mẫu biểu diễn bài viết (Blog)

Tạo thư mục con Models thêm một lớp là Article.cs

Pages/Article.cs

using System;
using System.ComponentModel.DataAnnotations;

namespace razor08.efcore.Models
{
    public class Article
    {
        public int ID { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime PublishDate { get; set; }

        public string Content {set; get;}
    }
}

Tạo Database Context

Tạo ra DbContext biểu diễn cơ sở dữ liệu sẽ làm việc (xem thêm tạo DbContext)

Trong thư mục Data tạo ra lớp ArticleContext

using Microsoft.EntityFrameworkCore;
using razor08.efcore.Models;

namespace razor08.efcore.Data
{
    public class ArticleContext : DbContext
    {
        public ArticleContext(DbContextOptions<ArticleContext> options) : base(options)
        {
            // Phương thức khởi tạo này chứa options để kết nối đến MS SQL Server
            // Thực hiện điều này khi Inject trong dịch vụ hệ thống
        }
        public DbSet<Article> Article {set; get;}
    }
}

Chuẩn bị MS SQL Server

Nếu đã có SQL Server thì bỏ qua bước này, nếu chưa có thì có thể nhanh chóng tọa ra một SQL Server bằng Docker (Học Docker), hướng dẫn tại MSSQL

Tải về docker-compose.yml, để vào một thư mục, rồi thực hiện lệnh:

docker up -d

Bạn đã có một SQL Server lắng nghe ở cổng 1433, tài khoản sa và password là Password123

Chuỗi kết nối nếu db đặt tên là articledb là: "Data Source=localhost,1433; Initial Catalog=articledb; User ID=SA;Password=Password123"

Đăng ký ArticleContext vào hệ thống ServiceCollection

Bạn mở file config của ứng dụng appsettings.json (về cấu hình ứng dụng xem tại: cấu hình ứng dụng ASP.NET Core), thêm vào nội dung thành:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*", 
  "ConnectionStrings": {
    "ArticleContext": "Data Source=localhost,1433; Initial Catalog=articledb; User ID=SA;Password=Password123"
  }
}

Ở đây đã thêm vào file cấu hình thông tin chuỗi kết nối: ConnectionStrings.ArticleContext

Đăng ký ArticleContext vào dịch vụ hệ thống bằng cách mở file Startup.cs và chỉnh sửa:

/.. 
using Microsoft.EntityFrameworkCore;

namespace razor08.efcore
{
    public class Startup
    {

        /..

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();

            services.AddDbContext<ArticleContext>(options => {
                string connectstring = Configuration.GetConnectionString("ArticleContext");
                options.UseSqlServer(connectstring);
            });
        }

        /..

    }
}

services.AddDbContext (mã nguồn EntityFrameworkServiceCollectionExtensions) là phương thức đăng ký dịch vụ DbContext, dịch vụ thuộc loại Scoped, nên chỉ lấy được khi một phạm vi được tạo ra.

Về cách lấy ra dịch vụ đã đăng ký xem tại: IServiceCollection và Inject

Từ đây bạn đã có thể lấy ra dịch vụ ArticleContext để thực hiện tương tác với SQL Server (database tên: articledb )

Tạo các trang thao tác CRUD của Model

Khi đã có Model Article bạn có thể tạo ra các trang để thực hiện việc tạo mới, cập nhật, xóa, hay xem (các thao tác này là CRUD). Tuy nhiên, nếu bạn dùng công cụ EF, bạn có thể phát sinh tự động các trang razor có chức năng này.

Thực hiện: Tạo CRUD là các trang razorpage, cho model Aricle, các trang sinh ra lưu tại Pages/Blog

dotnet aspnet-codegenerator razorpage -m Article -dc ArticleContext -udl -outDir Pages/Blog --referenceScriptLibraries

Cú pháp khác của dotnet aspnet-codegenerator xem tại: dotnet-aspnet-codegenerator

Nếu muốn phát trinh controller thì thay razorpage bằng controller

Sau khi chạy lệnh, nó tạo ra các trang trong Pages/Blog các trang kèm PageModel như: Blog/Create, Blog/Delete, Blog/Details, Blog/Edit, Blog/Index - bạn hãy mở code của chúng ra xem và sửa lại nếu muốn

Những trang này có thể truy cập qua địa chỉ https://localhost:5001/Blog, https://localhost:5001/Blog/Create ...

Tạo Database (migration)

Các cấu hình trên là làm việc trên ArticleContext tươnng ứng là Database có tên articledb (cấu hình chuỗi kết nối), tuy nhiên trong Server SQL chưa có CSDL. Do vậy, thực hiện migration (xem Tạo migration trong EntityFramework) như sau:

dotnet ef migrations add createdb
dotnet ef database update

Sau lệnh này nó tạo - cập nhật Db, có thể kiểm tra bằng Azure Data Studio

Như vậy đã có Database, giờ bạn đã có thể truy cập địa chỉ https://localhost:5001/Blog sẽ là trang hiện thị danh sách bài viết, https://localhost:5001/Blog/Create để tạo danh sách bài viết ...

Thay vì tạo thủ công một số Article để chèn vào Database ở Url https://localhost:5001/Blog/Create có thể chèn một số Article tự động như sau:

Tạo ra lớp InsertTestArticle với nội dung:

using System;
using Microsoft.Extensions.DependencyInjection;
using razor08.efcore.Data;
using System.Linq;

namespace razor08.efcore.Models
{
    public static class InsertTestArticle
    {
        public static void Initialize(IServiceProvider serviceProvider) {
           using(var context = serviceProvider.GetService<ArticleContext>())
           {
               if (context.Article.Any()) {
                   // Đã có dữ liệu
                   return;
               }

               context.AddRange(new Article[] {
                   new Article() {
                       Title = "Giới thiệu C# và viết chương trình CS đầu tiên",
                       PublishDate = DateTime.Parse("1-2-2020"),
                       Content = @"Giới thiệu C#, cài đặt .NET Core SDK, VSC và viết
                                   chương trình CS (C# CSharp) đầu tiên chạy đa nền tảng Windows,
                                   macOS, Linux, hàm Main trong C# và viết các
                                   comment - ghi chú - xml document"
                   },
                   new Article() {
                       Title = "Biến hằng số kiểu dữ liệu và nhập xuất dữ liệu C# .NET Core",
                       PublishDate = DateTime.Parse("2-2-2020"),
                       Content = @"Tìm hiểu về biến - hằng số, cách khai báo và khởi tạo biến
                                   cùng các kiểu dữ liệu cơ bản trong C#, khai báo biến kiểu
                                   ngầm định var, tiến hành nhập xuất dữ liệu với Console"
                   },
                   new Article() {
                       Title = "Các toán tử tính toán số học trong C# toán tử gán và tăng giảm",
                       PublishDate = DateTime.Parse("3-2-2020"),
                       Content = @"(C#) Khái niệm về toán tử, các loại toán tử số học như
                                   + - / *, thứ tự ưu tiên toán tự trong biểu thức,
                                   các loại toán tử gán và toán tử tăng giảm"
                   },
                   new Article() {
                       Title = "Toán tử so sánh logic và các câu lệnh if switch trong C# .NET",
                       PublishDate = DateTime.Parse("4-2-2020"),
                       Content = @"Giới thiệu C#, cài đặt .NET Core SDK, VSC và viết chương trình CS
                                   (C# CSharp) đầu tiên chạy đa nền tảng Windows, macOS,
                                   Các toán tử so sánh như so sánh bằng, so sánh lớn hơn ..
                                   cùng các phép toán logic và, hoặc hay phủ định,
                                   áp dụng viết câu lệnh điều kiện if else hay câu lệnh rẽ nhiều nhánh
                                   switch case trong lập trình C#"
                   },
                   new Article() {
                       Title = "Vòng lặp trong trong C# for do while và câu lệnh break continue",
                       PublishDate = DateTime.Parse("5-2-2020"),
                       Content = @"Tạo các vòng lặp for, while, do while trong C# và sử dụng câu lệnh
                                   .điều hướng vòng lặp continue, break"
                   },
               });
               context.SaveChanges();
           }
        }

    }
}

Bạn có thể chạy InsertTestArticle.InsertArticle khi khởi động ứng dụng, có thể trong Startup.cs, Program.cs. Ví dụ thực hiện trong hàm Main của Program.cs

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

    // Tạo một scope dịch vụ
    using (var scope = host.Services.CreateScope()) {
        InsertTestArticle.InsertArticle(scope.ServiceProvider);
    }


    host.Run();
}

Cập nhật và trình bày lại các trang

Các trang phát sinh ở thư mục /Pages/Blog bạn có thể tùy chỉnh theo mục đích của mình. Ở đây thực hiện cập nhật vài thông tin sau:

Trong các trang .cshtml có sử dụng @Html.DisplayFor để hiện thị tiêu đề của Model, bạn có thể thiết lập tiêu đề này thay cho tên mặc định của thuộc tính, sử dụng [Display], ví dụ:

public class Article
{
    public int ID { get; set; }

    [Display(Name="Tiêu đề")]
    public string Title { get; set; }

    [Display(Name="Ngày đăng")]
    [DataType(DataType.Date)]
    public DateTime PublishDate { get; set; }

    [Display(Name="Nội dung")]
    public string Content {set; get;}
}

Mở Blog/Index.cshtml có thể xóa bớt các trường cần hiện thị, trình bày lại theo nhu cầu. Ví dụ trình bày trang /Blog/

Sửa lại Route của các Page

Các liên kết bấm vào để xóa, sửa ... được thực hiện bởi TagHelper Anchor (xem phần trước), ví dụ:

<a asp-page="./Edit" asp-route-id="@item.ID">Sửa</a>

Thì nó phát sinh mã HTML tương ứng:

<ahref="/Blog/Edit?id=123">Sửa</a>

Giờ bạn ta sẽ cấu hình tham số ID là một phần của Route và các trang có Route tùy chọn riêng.

Sửa @page trong Blog/Index.cshtml thành:

@page "/blogs"

Giờ sẽ truy cập trang Index là https://localhost:5001/blogs

Tương tự thêm vào

  • Create.cshtml @page "/tao-bai-viet/"
  • Delete.cshtml @page "/xoa-bai-viet/{id:int}/"
  • Details.cshtml @page "/xem-bai-viet/{id:int}/"
  • Edit.cshtml @page "/sua-bai-viet/{id:int}/"

Bằng cách như vậy thay đổi được các Route.

Thêm chức năng Search

Ta sẽ thêm chức năng tìm kiếm theo tiêu đề Article vào trang Index

Sửa Index.cshtml.cs thành

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using razor08.efcore.Data;
using razor08.efcore.Models;
using Microsoft.Extensions.DependencyInjection;

namespace razor08.efcore.Pages.Blog
{
    public class IndexModel : PageModel
    {
        private readonly razor08.efcore.Data.ArticleContext _context;

        public IndexModel(razor08.efcore.Data.ArticleContext context)
        {
            _context = context;
        }

        public IList<Article> Article { get;set; }

        // Chuỗi để tìm kiếm, được binding tự động kể cả là truy 
        // cập get
        [BindProperty(SupportsGet = true)]
        public string SearchString { get; set; }
        public async Task OnGetAsync()
        {

            // Truy vấn lấy các Article
            var articles = from a in  _context.Article select a;
            if (!string.IsNullOrEmpty(SearchString))
            {
                Console.WriteLine(SearchString);
                // Truy vấn lọc các Article mà tiêu đề chứa chuỗi tìm kiếm
                articles = articles.Where(article => article.Title.Contains(SearchString));
            }
            // Đọc (nạp) Article
            Article = await articles.ToListAsync();
        }
    }
}

Thêm Form nhập chuỗi tìm kiếm trong Index.cshtml

<form method="get" asp-page="./Index">
    <p>
        Title: <input type="text" asp-for="SearchString" />
        <input type="submit" value="Tìm" />
    </p>
</form>

Mã nguồn ASP_NET_CORE/razor08.efcore


Đăng ký nhận bài viết mới