Xây dựng phân trang hiệu quả trong Asp.Net

Introduction

Bài viết này sẽ giải thích tại sao sử dụng phân trang cho chỉ các records được yêu cầu, và hiển thị chúng dưới dạng paging và có reference tới tổng số records.

Background

Rất có thể bạn đã gặp phải vấn đề mà bạn cần liệt kê một vài record từ nguồn dữ liệu có chứa vài nghìn hoặc nhiều hơn records sau đó nhận thấy rằng phân trang là một yếu tố quan trọng để cải thiện hiệu suất trang web. Bắt đầu từ việc lọc dữ liệu đến chọn các bản ghi liên quan từ cơ sở dữ liệu đến hiển thị  phân trang, có một vài bước quan trọng nhưng cần xem xét để xây dựng một hệ thống phân trang đáng tin cậy.

Creating the project

Ta sẽ để mặc định ASP.Net Core 2.2 project template sử dụng VS2019, 
Trước khi đào sâu tới phân trang ta cần tạo datasource cho các records test. Datasource cần chứa nhiều records vậy sẽ thấy được lợi ích thực sự của việc phân trang. Theo thứ tự trước hết focus vào topic phân trang. Ta sẽ có 1 list items (CultureInfo) cái đã có sẵn trong famework.

Open Pages/Index.cshtml.cs and add the data source as below:

public class IndexModel : PageModel
{      
    public CultureInfo[] CulturesList { get; set; }
    private CultureInfo[] Cultures { get; set; }

    public IndexModel()
    {
        //this will act as the main data source for our project
        Cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
    }

    public void OnGet()
    {
        CulturesList = Cultures;
    }
}

Open Pages/Index.cshtml and add the below code to display the cultures list:

<table class="table table-striped">
    <thead>
        <tr>
            <th>LCID</th>
            <th>English Name</th>
            <th>Native Name</th>
            <th>Culture types</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var c in Model.CulturesList)
        {
            <tr>
                <td>@c.LCID</td>
                <td>@c.EnglishName</td>
                <td>@c.NativeName</td>
                <td>@c.CultureTypes</td>
            </tr>
        }
    </tbody>
</table>

Run the app to see the first results;

Image 1

Handling paging on the backend

Như bạn đã biết việc gửi toàn bộ records tới view là cách làm không hiệu quả trong lập trình, vì thế ta sẽ limit số lượng records được select và sẽ gửi tới view một số lượng nhỏ bản ghi hơn. Theo các đơn giản ta cần 2 biến cho phân trang.
- Page number : a variable to indicate the requested page number
- Page size : a variable to indicate the total numbers of records that should be selected at once
later we will add more variables for filtering as well.
Back to Pages/Index.cshtml.cs file, define the variables and modify OnGet, so our new IndexModel will look like below:

public class IndexModel : PageModel
{      
    public IList<CultureInfo> CulturesList { get; set; }
    private CultureInfo[] Cultures { get; set; }

    //page number variable
    [BindProperty(SupportsGet = true)]
    public int P { get; set; } = 1;

    //page size variable
    [BindProperty(SupportsGet = true)]
    public int S { get; set; } = 10;

    public IndexModel()
    {
        //this will act as the main data source for our project
        Cultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
    }

    public void OnGet()
    {
        CulturesList = Cultures
            //make sure to order items before paging
            .OrderBy(x=>x.EnglishName)

            //skip items before current page
            .Skip((P-1)*S)

            //take only 10 (page size) items
            .Take(S)
            
            //call ToList() at the end to execute the query and return the result set
            .ToList();
    }
}

Run the app, you will see first 10 records only,

Improving search logic 

- Ta sử dụng 1 keyword trong việc search. Ta có thể cải tiến query bằng việc phân tách keywords và xóa bỏ khoảng trắng và các từ trùng lặp như sau:

var _keyWords = Q.Split(new[] { ' ', ',', ':' }, StringSplitOptions.RemoveEmptyEntries).Distinct();

- Khi sử dụng hệ cơ sở dự liệu MSSqlDb và thực hiện việc search trong nullable fields ta có thể nhận error khi field bị null, để tránh null field ta có thể thêm điều kiện check null cho search logic.

var query = Cultures
    //search in EnglishName and NativeName
    .Where(x => _keyWords.Any(kw =>
            (x.EnglishName!=null && x.EnglishName.Contains(kw, StringComparison.OrdinalIgnoreCase)) ||
            (x.NativeName != null && x.NativeName.Contains(kw, StringComparison.OrdinalIgnoreCase))))

- Thực tế có thể cải thiện thêm hiệu suất bằng cách sử dụng  AsNoTracking() khi tìm kiếm database, vì thế famework sẽ không giữ lại các entites đã chọn và sẽ giải phóng bộ nhớ.

Nhận xét

Bài đăng phổ biến từ blog này

Material Tree

.Net secure coding guidelines

Tìm hiểm Checkbox Angular Material