본문 바로가기
Entity Framework Core

.NET Core MVC] EF Core - 3.정렬, 검색, 페이징

by Fastlane 2023. 8. 1.
728x90
반응형

StudentsController.cs

    public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber)
    {
       
        int pageSize = 3;


        if(searchString != null)
        {
            pageNumber = 1;
        }
        else
        {
            searchString = currentFilter;


        }
        ViewData["CurrentSort"] = sortOrder;
        ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
        ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
        ViewData["CurrentFilter"] = searchString;


        //IQueryable 변수는 db로 전달되는 쿼리가 아니다.
        //ToListAsync() 함수가 호출되어 collection으로 변환해주기 전까지 실행되지 않는다.
        var students = from s in _context.Students
                        select s;


        if(!String.IsNullOrEmpty(searchString))
        {
            students = students.Where(s => s.LastName.Contains(searchString)
                                    || s.FirstMidName.Contains(searchString));
                                   
        }


        switch(sortOrder)
        {
            case "name_desc":
                students = students.OrderByDescending(s => s.LastName);
                break;
            case "Date":
                students = students.OrderBy(s => s.EnrollmentDate);
                break;
            case "date_desc":
                students = students.OrderByDescending(s => s.EnrollmentDate);
                break;
            default:
                students = students.OrderBy(s => s.LastName);
                break;
        }


        //return View(await students.AsNoTracking().ToListAsync());


        return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));
    }

PaginatedList.cs

using Microsoft.EntityFrameworkCore;


namespace WEB;


public class PaginatedList<T> : List<T>
{
    public int PageIndex { get; private set; }
    public int TotalPages { get; private set; }


    public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        TotalPages = (int)Math.Ceiling(count / (double)pageSize);


        this.AddRange(items);
    }


    public bool HasPreviousPage => PageIndex > 1;


    public bool HasNextPage => PageIndex < TotalPages;


    //constructor는 비동기코드를 실행할 수 없어서
    //CreateAsync함수가 PaginatedList<T> object를 만드는 constructor를 대신해 사용되었다.
    public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
    {
        var count = await source.CountAsync();
        var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
        return new PaginatedList<T>(items, count, pageIndex, pageSize);
    }
}

Index.cshtml

@model PaginatedList<WEB.Models.Student>


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


<h2>Index</h2>


<p>
    <a asp-action="Create">Create New</a>
</p>
<form asp-action="Index" method="get">
    <div class="form-actions no-color">
        <p>
            Find by name: <input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" />
            <input type="submit" value="Search" class="btn btn-default" />
            <a asp-action="Index">Back to Full List</a>
        </p>
    </div>
</form>
<table class="table">
    <thead>
        <tr>
                <th>
                    <a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">LastName</a>
                </th>
                <th>
                    First Name
                </th>
                <th>
                    <a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Enrollment Date</a>
                </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </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>


@{
    var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
    var nextDisabled = !Model.HasNextPage ? "disabled" : "";
}


<a asp-action="Index"
    asp-route-sortOrder="ViewData["CurrentSort"]"
    asp-route-pageNumber="@(Model.PageIndex - 1)"
    asp-route-currentFilter="@ViewData["CurrentFilter"]"
    class="btn btn-default @prevDisabled">
    Previous
</a>


<a asp-action="Index"
    asp-route-sortOrder="ViewData["CurrentSort"]"
    asp-route-pageNumber="@(Model.PageIndex + 1)"
    asp-route-currentFilter="@ViewData["CurrentFilter"]"
    class="btn btn-default @nextDisabled">
    Previous
</a>

 

코드 단순화를 위한 dynamic LINQ

위에서 정렬을 위해, switch 구문에 컬럼명을 하드코딩 해서 LINQ를 사용하였다. 2개의 컬럼에 대해서는 잘 동작한다. 하지만, 많은 컬럼을 처리해야 하는 경우에는, EF.Property 함수를 이용해서 property 이름을 string으로 명시할 수 있다. 

    public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? pageNumber)
    {
       
        int pageSize = 10;


        if(searchString != null)
        {
            pageNumber = 1;
        }
        else
        {
            searchString = currentFilter;


        }
        ViewData["CurrentSort"] = sortOrder;
        //ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
        //ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
        ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
        ViewData["DateSortParm"] = sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";
        ViewData["CurrentFilter"] = searchString;


        //IQueryable 변수는 db로 전달되는 쿼리가 아니다.
        //ToListAsync() 함수가 호출되어 collection으로 변환해주기 전까지 실행되지 않는다.
        var students = from s in _context.Students
                        select s;


        if(!String.IsNullOrEmpty(searchString))
        {
            students = students.Where(s => s.LastName.Contains(searchString)
                                    || s.FirstMidName.Contains(searchString));
                                   
        }


        /*
        switch(sortOrder)
        {
            case "name_desc":
                students = students.OrderByDescending(s => s.LastName);
                break;
            case "Date":
                students = students.OrderBy(s => s.EnrollmentDate);
                break;
            case "date_desc":
                students = students.OrderByDescending(s => s.EnrollmentDate);
                break;
            default:
                students = students.OrderBy(s => s.LastName);
                break;
        }
        */
        if (string.IsNullOrEmpty(sortOrder))
        {
            sortOrder = "LastName";
        }


        bool descending = false;
        if (sortOrder.EndsWith("_desc"))
        {
            sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
            descending = true;
        }


        if (descending)
        {
            students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
        }
        else
        {
            students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
        }


        //return View(await students.AsNoTracking().ToListAsync());


        return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));
    }

 

728x90
반응형

댓글