Asp.Net Core kết nối cơ sở dữ liệu (Phần II)

0 Comments

Sau khi thêm một cơ sở dữ liệu, tới đây mọi thứ đã sẵn sàng cho việc sử dụng context trong lớp dịch vụ.

Khởi tạo một lớp dịch vụ mới:

Ở phần trước, bạn đã tạo một FakeTodoItemService nơi lưu trữ danh sách việc cần làm được tạo cứng. Hiện tại bạn đã có database context, bạn có thể khởi tạo một lớp service mới sử dụng Entity Framework Core để lấy các mục công việc từ database.

Xóa tệp FakeTodoItemService.cs, và khởi tạo một tệp mới:
Services/TodoItemService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AspNetCoreTodo.Data;
using AspNetCoreTodo.Models;
using Microsoft.EntityFrameworkCore;

namespace AspNetCoreTodo.Services
{
       public class TodoItemService : ITodoItemService
       {
             private readonly ApplicationDbContext _context;

             public TodoItemService(ApplicationDbContext context)
             {
                     _context = context;
             }

             public async Task<TodoItem[]> GetIncompleteItemsAsync()
             {
                   var items = await _context.Items
                            .Where(x => x.IsDone == false)
                            .ToArrayAsync();
                   return items;
            }
       }
}

Bạn sẽ thấy Dependency Injection tương tự mà bạn đã sử dụng trong phần trước, ở đây Dependency injection được sử dụng cho ApplicationDbContext. ApplicationDbContext đã được thêm vào phân vùng dịch vụ trong phương thức ConfigureService, vì vậy nó luôn sẵn sàng để sử dụng tại đây.

Chúng ta hãy cùng nhau phân tích chi tiết phương thức GetIncompleteItemSync. Đầu tiên, nó sủ dụng thuộc tính Item của context để truy cập tất cả các mục việc cần làm trong DbSet:
var items = await _context.Items

Sau đó phương thức “where” được sử dụng để lọc ra những mục chưa hoàn thành.
.Where(x => x.IsDone == false)

Phương thức Where là một tính năng của C# được gọi là LinQ giúp dẽ dàng thực hiện truy vấn tới cơ sở dữ liệu. Ở đây Entity Framework Core chuyển phương thức where thành một câu lệnh “SELECT * FROM Items WHERE IsDone = 0” hoặc một câu lệnh truy vấn tương tự trong NoSQL.

Cuối cùng là phương thức ToArrayAsync nói với Entity FrameWork Core để lấy tất cả các thực thể phù hợp với điều kiện lọc và trả lại dưới dạng mảng. Phương thức ToArrayAsync không đồng bộ vì vậy nó phải được chờ để nhận giá trị.

Cập nhật phân vùng Service:

Vì bạn đã xóa lớp FakeTodoItemService, nên bạn cần phải cập nhật dòng kết nối với interfae trong configureService:
services.AddScoped<ITodoItemService, TodoItemService>();

AddScoped thêm dịch vụ của bạn vào phân vùng chứa dịch vụ bằng vòng đời có phạm vi. Điều này có nghĩa là một phiên bản mới của lớp TodoItemService sẽ được tạo trong mỗi yêu cầu. Điều này là cần thiết cho các lớp dịch vụ tương tác với cơ sở dữ liệu.

Test ứng dụng:
Khởi động ứng dụng và điều hướng tới http://localhost:5000/todo. Các mục dữ liệu giả đã mất và ứng dụng đang thực hiện các truy vấn thực đối với cơ sở dữ liệu. Không có bất kỳ mục nào trong cơ sở dữ liệu, vì vậy ở đây sẽ trống.

Thêm các Items mới:
Thêm một form:
Trong Views/Todo/Index.cshtml tìm đến:
<div class=”panel-footer add-item-form”>
          <!– TODO: Add item form –>
</div>
Để giữ mọi thứ riêng biệt và dễ quản lý chúng ta sẽ tạo form dưới dạng Partial View. Partial View là một phần nhỏ của view được viết riêng trong một tệp.
Khởi tạo view AddItemPartial.cshtml:
@model TodoItem

<form asp-action=”AddItem” method=”POST”>
        <label asp-for=”Title”>Add a new item:</label>
        <input asp-for=”Title”>
        <button type=”submit”>Add</button>
</form>
Thẻ trợ giúp asp-action có thể tạo URL cho form, giống như khi sử dụng URL trên thẻ <a>. Trong trường hợp này, thẻ trợ giúp asp-action được thay thế bằng đường dẫn thực tới hành động AddItem bạn sẽ tạo:

<form action=”/Todo/AddItem” method=”POST”>

Thêm thẻ trợ giúp asp vào thẻ <form> cũng thêm trường ẩn vào form có chứa mã thông báo xác minh. Mã thông báo xác minh này có thể được sử dụng để ngăn chặn các cuộc tấn công giả mạo yêu cầu trên trang web (CSRF). Bạn sẽ xác minh mã thông báo khi bạn viết hành động.

Thêm đoạn mã sau vào View chính để liên kết chúng với partial view:
Views/Todo/Index.cshtml
<div class=”panel-footer add-item-form”>
         @await Html.PartialAsync(“AddItemPartial”, new TodoItem())
</div>

Thêm một hành động:
Khi người dùng nhấp vào nút Thêm, trình duyệt sẽ tạo một yêu cầu POST tới Todo/AddItem trong controller. Điều đó không hoạt động ngay lúc này, vì bạn chưa viết bất kỳ đoạn mã nào xử lý chúng trong controller. Bạn cần khởi tạo một hành động mới trong TodoController:
[ValidateAntiForgeryToken]
public async Task<IActionResult> AddItem(TodoItem newItem)
{
        if (!ModelState.IsValid)
        {
                return RedirectToAction(“Index”);
        }

        var successful = await _todoItemService.AddItemAsync(newItem);
        if (!successful)
       {
            return BadRequest(“Could not add item.”);
       }

       return RedirectToAction(“Index”);
}
Lưu ý cách mà hành động addItem chấp nhận một tham số TodoItem. Đây là model TodoItem giống như bạn đã tạo trong phần trước để lưu trữ thông tin về một công việc cần làm. Khi được sử dụng ở đây như một tham số hành động Asp.Net Core sẽ tự động thưc hiện một quy trình gọi là model binding. Model binding xem xét dữ liệu trong một yêu cầu và kết hợp chúng với các thuộc tính trên model. Nói cách khác, khi người dùng gửi dữ liệu từ Form lên Asp.Net Core sẽ lấy thông tin và đặt nó vào biến newItem.

Thuộc tính [ValidateAntiForgeryToken] trước khi hành động cho Asp.Net Core biết rằng nó sẽ tìm và xác minh mã ẩn được thêm vào form bởi thẻ trợ giúp asp-action. Đây là một biện pháp quan trọng để ngăn chặn các cuộc tấn công giả mạo chéo trang (CSRF), nơi mà người dùng có thể bị lừa gửi dữ liệu từ một trang web độc hại. Mã thông báo xác minh đảm bảo rằng ứng dụng của bạn thực sự là ứng dụng đang được hiển thị và gửi form.

Hãy xem lại giao diện AddItemPartial.cshtml. Dòng @model TodoItem ở đầu tệp cho Asp.Net Core biết rằng view được ghép nối với model TodoItem. Điều này cho phép sử dụng asp-for=”title” trên thẻ <input> để Asp.Net Core biết rằng phần tử đâu vào này là dành cho thuộc tính Title.

Bởi vì dòng @model, partial view sẽ được thông qua một đối tượng TodoItem khi được hiển thị. Truyền cho nó một TodoItem mới thông qua Html.PartialAsync khởi tạo form với một mục trống.

Trong model binding, mọi thuộc tính model không thể khớp với các trường trong request sẽ được bỏ qua. Vì form chỉ tạo một đầu vào Title, nên các thuộc tính khác sẽ trống hoặc chứa giá trị mặc định.

Sau khi ràng buộc dữ liệu yêu cầu vào model, Asp.Net Core cũng thực hiện xác nhận model. Xác nhận kiểm tra xem các dữ liệu đầu vào có hợp lệ hay không. Bạn có thể thêm các thuôc tính vào model để báo cho Asp.Net Core biết nó nên được xác nhận như thế nào.

Thuộc tính [Required] trên thuộc tính Title báo cho trình xác thực kiểm tra xem thuộc tính Title có null hay không. Hãy xem mã của hành động AddItem: Khối đầu tiên kiểm tra xem ModelState có hợp lệ hay không:

if (!ModelState.IsValid)
{
          return RedirectToAction(“Index”);
}
Nếu modelState không hợp lệ vì bất kỳ lý do gì, trình duyệt sẽ được chuyển hướng tới đường dẫn: /Todo/Index.Tiếp theo, controller gọi vào lớp dịch vụ để thực hiện thao tác cơ sở dữ liệu lưu công việc mới. Nếu không thành công, hành động sẽ trả về thông báo lỗi. Cuối cùng nếu mọi thứ hoàn toàn không có lỗi, hành động sẽ được chuyển tới đường dẫn /Todo/Index làm mới trang và hiển thị danh sách công việc.

Thêm một phương thức dịch vụ:
Nếu bạn đang sử dụng một trình soạn thảo hiểu ngôn ngữ C#, bạn sẽ nhận được một cảnh báo đỏ dưới AddItemAsync bởi vì phương thức này chưa tồn tại. Bạn cần thêm phương thức tới lớp dịch vụ. Đầu tiên hãy thêm nó tới interface định nghĩa trong ItoDoItemService:

public interface ITodoItemService
{
         Task<TodoItem[]> GetIncompleteItemsAsync();

         Task<bool> AddItemAsync(TodoItem newItem);
}

Sau đó triển khai nó thực tế ở lớp ItodoItemService:
public async Task<bool> AddItemAsync(TodoItem newItem)
{
        newItem.Id = Guid.NewGuid();
        newItem.IsDone = false;
        newItem.DueAt = DateTimeOffset.Now.AddDays(3);

       _context.Items.Add(newItem);

       var saveResult = await _context.SaveChangesAsync();
       return saveResult == 1;
}
Thuộc tính newItem.Title đã được thiết lập bởi Asp.Net Core model binding, vì vậy phương thức này chỉ cần gán ID và đặt giá trị mặc định cho các thuộc tính khác. Sau đó mục mới sẽ được thêm vào database context. Nó chỉ thực sự được lưu khi bạn gọi phương thức SaveChangeAsync(). Nếu lưu thành công phương thức sẽ trả lại 1.

Tới lúc này bạn có thể chạy chương trình lên và thêm vào những ghi chú cần thiết cho bạn.

Categories:

Leave a Reply

Your email address will not be published. Required fields are marked *