-->

#1 Tìm hiểu CRUD trong ASP.NET Core MVC

Khi bạn bắt đầu với một dự án thực tế nào, đều phải dùng đến CRUD, một phần không thể thiếu trên các dự án. Vậy CRUD là gì? Ta dùng nó trên ASP.NET Core như thế nào? Hãy cùng tìm hiểu ở bài viết này.

1. CRUD là gì?

CRUD viết tắt cho 4 hành động liên quan tới database là Create, Read, Update và Delete. Cụ thể thí dụ như sau:

  • Create – tạo mới bài viết, tạo mới người dùng,...
  • Read – đọc nội dung bài viết, đọc thông tin người dùng, đọc thông tin đơn hàng, xem sản phẩm,...
  • Update – chỉnh sửa bài viết, cập nhật giá sản phẩm, người dùng đổi mật khẩu,...
  • Delete – xóa bài viết, xóa bình luận, xóa đơn hàng lỗi,...
Vậy CRUD là gì? CRUD là 4 tính năng chính để làm việc với Cơ Sở Dữ Liệu. Hầu hết mọi tính năng trên Website đều có liên quan đến CRUD. Vậy làm việc với CRUD như thế nào, hãy cùng tiếp tục tìm hiểu.

1.1. DbContext

Entity Framework cho phép bạn có thể query, insert, update ,delete dữ liệu thông qua DbContext. Hay nói cách khác Entity Framework sử dụng DbContext để kết nối dữ liệu với Database. Nó là trung gian kết nối giữa các Entities sinh ra trong ứng dụng của bạn và Database.

Về mặt logic, một DbContext ánh xạ tới một cơ sở dữ liệu cụ thể có một lược đồ mà DbContext hiểu được. Và trên lớp DbContext đó, bạn có thể tạo các thuộc tính là loại Dbset <T>. Tham số loại chung T sẽ là một loại thực thể như Nhân viên là một thực thể trong ứng dụng FirstAppDemo.

1.2. Create

Để tạo 1 record trong database, đối với Entity Framework, chuyện rất đơn giản:

MyDbContext db = new MyDbContext();

public ActionResult Create()
{
    return View();
}

[HttpPost]
public ActionResult CreateDoctor(Doctors doctor){
    db.Doctors.Add(doctor);
    db.SaveChanges();
    return RedirectToAction("Index", "Doctors");
}

Entity Framework có 1 cách rất hay (và hơi phức tạp) để có thể tối ưu hóa các phương thức truy xuất/cập nhật dữ liệu: Nó sẽ theo dõi các thay đổi của 1 object

Giả sử, object của bạn có 50 field. Bạn chỉ thay đổi 1 field trong đó, rồi gọi db.SaveChanges(), EF sẽ biết được rằng chỉ có 1 field bị thay đổi, và nó sẽ cập nhật đúng 1 field duy nhất, tăng hiệu suất của ứng dụng.

1.3. Read

Để lấy 1 dòng dữ liệu từ 1 bảng, bạn có thể dùng method Find.

Để lấy nhiều dòng dữ liệu thỏa một điều kiện nào đó, bạn có thể dùng LINQ.

Hầu như tất cả các query bạn quen thuộc khi viết câu truy vấn bằng SQL đều có thể viết được dưới dạng LINQ. Đây gọi là LINQ to Entities (Dùng Linq để query thông qua các entities trong Entity Framework)

1.4. Update

Không như các lệnh vừa rồi, việc update dùng Entity Framework hơi phức tạp một chút, nhưng hãy tìm hiểu qua cách này:

public ActionResult Update(int id){
    return View(db.Doctors.Where(s => s.Id == id).First());
}

[HttpPost]
public ActionResult UpdateDoctor(Doctors doctor){
    Doctors d = db.Doctors.Where(s => s.Id == doctor.Id).First();
    d.Name = doctor.Name;
    d.Phone = doctor.Phone;
    d.Specialist = doctor.Specialist;
    db.SaveChanges();
    return RedirectToAction("Index", "Doctors");
}

1.5. Delete

Cũng như update, bạn cần tìm đến id để delete object, ví dụ như sau:

[HttpPost]
public bool Delete(int id){
try
{
    Doctors doctor = db.Doctors.Where(s => s.Id == id).First();
    db.Doctors.Remove(doctor);
    db.SaveChanges();
    return true;
}
catch (System.Exception)
{
    return false;
}

2. Hướng dẫn tạo CRUD trong ASP.NET Core MVC

Bước 1: Đầu tiên, tạo mới project ASP.NET Core Application (3.x), đặt tên là CRUD.Demo.

Bước 2: Chọn project với Web Application (Model-View-Controller).

Bước 3: Project này mình dùng Entity Framework Core Database First, nên bạn cần phải tạo 1 database trong SQL Server, cụ thể như sau:

create database crud_demo
go
use crud_demo
go
create table doctors
(
 id    int primary key,
 [Name]    nvarchar(256),
 Email    nvarchar(256),
 [Password]   nvarchar(256),
 Phone    varchar(15),
 Gender    int,
 Specialist   nvarchar(256),
 CreatedOn   datetime,
 [Status]   bit
)

Bước 4: Thêm ConnectionStrings vào appsettings.json, kết quả như sau:

"ConnectionStrings": {
    "ConnectionString": "Server=CNPM-PC\\SQLEXPRESS;Database=crud_demo;Trusted_Connection=True;MultipleActiveResultSets=true"
 }

  • Sau đó thay đổi Server name của các bạn.

Kết quả như sau:
crud-in-asp-net-core-mvc--hieuquoccom


Bước 5: Cài các NuGet Packages sau, bạn có thể nhấp chuột phải vào Dependencies > Manage NuGet Packages > Browse > sau đó tìm kiếm các NuGet Packages và cài:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Relational
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Design

Bước 6: Tạo mới một folder tên là Entities.

Bước 7: Trên thanh công cụ, vào Tools > Nuget Package Manager > Package Manager Console và thực thi đoạn mã sau:

Scaffold-DbContext "Server=CNPM-PC\SQLEXPRESS;Database= crud_demo; user id= sa;password= 123;Trusted_Connection=True;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Entities

crud-in-asp-net-core-mvc--hieuquoccom


Lưu ý: Bạn cần đổi tên Server, user id và password của bạn.

Kết quả là Entity sinh ra như sau:
crud-in-asp-net-core-mvc--hieuquoccom

Bước 8: Tạo mới 1 controller (empty) có tên là DoctorsController.cs, với code sau:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using CRUD.Demo.Entities;

namespace CRUD.Demo.Controllers
{
    public class DoctorsController : Controller
    {
        private readonly crud_demoContext _context;

        public DoctorsController(crud_demoContext context)
        {
            _context = context;
        }

        // GET: Doctors
        public async Task<IActionResult> Index()
        {
            return View(await _context.Doctors.ToListAsync());
        }

        // GET: Doctors/Details/5
        public async Task<IActionResult> Details(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var doctors = await _context.Doctors
                .FirstOrDefaultAsync(m => m.Id == id);
            if (doctors == null)
            {
                return NotFound();
            }

            return View(doctors);
        }

        // GET: Doctors/Create
        public IActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Create([Bind("Id,Name,Email,Password,Phone,Gender,Specialist,CreatedOn,Status")] Doctors doctors)
        {
            if (ModelState.IsValid)
            {
                _context.Add(doctors);
                await _context.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            return View(doctors);
        }

        // GET: Doctors/Edit/5
        public async Task<IActionResult> Edit(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var doctors = await _context.Doctors.FindAsync(id);
            if (doctors == null)
            {
                return NotFound();
            }
            return View(doctors);
        }

        // POST: Doctors/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Edit(int id, [Bind("Id,Name,Email,Password,Phone,Gender,Specialist,CreatedOn,Status")] Doctors doctors)
        {
            if (id != doctors.Id)
            {
                return NotFound();
            }

            if (ModelState.IsValid)
            {
                try
                {
                    _context.Update(doctors);
                    await _context.SaveChangesAsync();
                }
                catch (DbUpdateConcurrencyException)
                {
                    if (!DoctorsExists(doctors.Id))
                    {
                        return NotFound();
                    }
                    else
                    {
                        throw;
                    }
                }
                return RedirectToAction(nameof(Index));
            }
            return View(doctors);
        }

        // GET: Doctors/Delete/5
        public async Task<IActionResult> Delete(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }

            var doctors = await _context.Doctors
                .FirstOrDefaultAsync(m => m.Id == id);
            if (doctors == null)
            {
                return NotFound();
            }

            return View(doctors);
        }

        // POST: Doctors/Delete/5
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> DeleteConfirmed(int id)
        {
            var doctors = await _context.Doctors.FindAsync(id);
            _context.Doctors.Remove(doctors);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        private bool DoctorsExists(int id)
        {
            return _context.Doctors.Any(e => e.Id == id);
        }
    }
}

Bước 9: Bạn cần add View cho từng method, trước hết trong Views > tạo folder tên Doctors > tạo các view, cụ thể như sau:

Index.cshtml

@model IEnumerable<CRUD.Demo.Entities.Doctors>

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

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Email)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Password)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Phone)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Gender)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Specialist)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.CreatedOn)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Status)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Email)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Password)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Phone)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Gender)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Specialist)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.CreatedOn)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Status)
            </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>

Create.cshtml
@model CRUD.Demo.Entities.Doctors

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

<h1>Create</h1>

<h4>Doctors</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Create">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Id" class="control-label"></label>
                <input asp-for="Id" class="form-control" />
                <span asp-validation-for="Id" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Email" class="control-label"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Phone" class="control-label"></label>
                <input asp-for="Phone" class="form-control" />
                <span asp-validation-for="Phone" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Gender" class="control-label"></label>
                <input asp-for="Gender" class="form-control" />
                <span asp-validation-for="Gender" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Specialist" class="control-label"></label>
                <input asp-for="Specialist" class="form-control" />
                <span asp-validation-for="Specialist" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="CreatedOn" class="control-label"></label>
                <input asp-for="CreatedOn" class="form-control" />
                <span asp-validation-for="CreatedOn" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Status" class="control-label"></label>
                <input asp-for="Status" class="form-control" />
                <span asp-validation-for="Status" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Details.cshtml
@model CRUD.Demo.Entities.Doctors

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

<h1>Details</h1>

<div>
    <h4>Doctors</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Name)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Email)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Email)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Password)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Password)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Phone)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Phone)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Gender)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Gender)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Specialist)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Specialist)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.CreatedOn)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.CreatedOn)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Status)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Status)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

Delete.cshtml
@model CRUD.Demo.Entities.Doctors

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

<h1>Delete</h1>

<h3>Are you sure you want to delete this?</h3>
<div>
    <h4>Doctors</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Name)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Name)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Email)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Email)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Password)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Password)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Phone)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Phone)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Gender)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Gender)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Specialist)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Specialist)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.CreatedOn)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.CreatedOn)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Status)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Status)
        </dd>
    </dl>
    
    <form asp-action="Delete">
        <input type="hidden" asp-for="Id" />
        <input type="submit" value="Delete" class="btn btn-danger" /> |
        <a asp-action="Index">Back to List</a>
    </form>
</div>

Edit.cshtml
@model CRUD.Demo.Entities.Doctors

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

<h1>Edit</h1>

<h4>Doctors</h4>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Edit">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <input type="hidden" asp-for="Id" />
            <div class="form-group">
                <label asp-for="Name" class="control-label"></label>
                <input asp-for="Name" class="form-control" />
                <span asp-validation-for="Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Email" class="control-label"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Phone" class="control-label"></label>
                <input asp-for="Phone" class="form-control" />
                <span asp-validation-for="Phone" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Gender" class="control-label"></label>
                <input asp-for="Gender" class="form-control" />
                <span asp-validation-for="Gender" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Specialist" class="control-label"></label>
                <input asp-for="Specialist" class="form-control" />
                <span asp-validation-for="Specialist" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="CreatedOn" class="control-label"></label>
                <input asp-for="CreatedOn" class="form-control" />
                <span asp-validation-for="CreatedOn" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Status" class="control-label"></label>
                <input asp-for="Status" class="form-control" />
                <span asp-validation-for="Status" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Save" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-action="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Bước 10: Các bạn vào Starup.cs và tìm đến phương thức ConfigureServices và thêm code sau:

services.AddDbContext<crud_demoContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("ConnectionString")));

Bước 11: Các bạn debug và chạy project và trải nghiệm kết quả:
crud-in-asp-net-core-mvc--hieuquoccom

3. Lời kết

Bài viết này giới thiệu tổng quan, và hướng dẫn làm CRUD cơ bản, cũng là mở màng trong series CRUD trong ASP.NET Core MVC.

Trong những bài sau, mình sẽ hướng dẫn code tối ưu và kết hợp với những công nghệ mới thư viện mới. Dự là một loạt bài viết đang mong chờ sắp tới.

Nếu gặp lỗi có thể cmt ở dưới, hoặc có thể kết nối facebook, gmail của mình.

Mong bài viết có ích với bạn, chúc các bạn thành công.

By Hiếu Quốc.

Có lẽ bạn thích?

1 comment

  1. Bạn viết rất dễ hiểu. cám ơn bạn nhiều.

    ReplyDelete

Post a Comment