- Entity Framework ASP.NET Core
- Chuẩn bị Model mẫu
- Tạo database context
- Chuẩn bị SQL Server
- Đăng ký DbContext vào dịch vụ hệ thống
- Tạo các trang thao tác CRUD của Model
- Tạo và cập nhật Database (migration)
- Cập nhật các trang
- Thêm chức năng Search
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.Design và Microsoft.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