Как отсортировать список объектов с помощью ViewModel вместо ViewBag?

Я использую ViewBag, чтобы отсортировать список студентов, найденных в списке классов. Я читал, что ViewBag - это то, чего следует избегать любой ценой, пытаясь создать правильный проект MVC.

При просмотре страницы, созданной приведенным ниже кодом, можно отсортировать список студентов различными способами (имя по альфа-каналу, фамилия по альфа-каналу, дата зачисления и т. Д.) И просматривать только ограниченное количество учащихся. на страницу.

Я не знаю точно, как перевести мой код для использования ViewModel вместо моего текущего дизайна.

Я использую следующий код:

Модель (Студент):

public class Student
{
    public int StudentID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public string Email { get; set; }
    public DateTime EnrollmentDate { get; set; } 
    public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Модель (зачисление):

public class Enrollment
{
    public int EnrollmentID { get; set; }
    public int CourseID { get; set; }
    public int StudentID { get; set; }
    public string Grade { get; set; } // pass, fail, incomplete
    public virtual Course Course { get; set; } 
    public virtual Student Student { get; set; } 
}

(У меня также есть модель Course, но на нее не ссылается непосредственно контроллер ниже, поэтому я опущу ее здесь - если необходимо показать ее детали, сообщите мне.)

Контроллер:

public class StudentController : Controller
{
    private SchoolContext db = new SchoolContext();

    //
    // GET: /Student/

    public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
    {
        ViewBag.CurrentSort = sortOrder;
        ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "Name desc" : "";
        ViewBag.DateSortParm = sortOrder == "Date" ? "Date desc" : "Date";
        ViewBag.FNameSortParm = sortOrder == "FName" ? "FName desc" : "FName";
        ViewBag.EmailSortParm = sortOrder == "Email" ? "Email desc" : "Email";

        if (Request.HttpMethod == "GET")
        {
            searchString = currentFilter;
        }
        else
        {
            page = 1;
        }
        ViewBag.CurrentFilter = searchString;

        var students = from s in db.Students
                       select s;
        if (!String.IsNullOrEmpty(searchString))
        {
            students = students.Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper())
                                   || s.FirstMidName.ToUpper().Contains(searchString.ToUpper()));
        }
        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;
            case "FName":
                students = students.OrderBy(s => s.FirstMidName);
                break;
            case "FName desc":
                students = students.OrderByDescending(s => s.FirstMidName);
                break;
            case "Email":
                students = students.OrderBy(s => s.Email);
                break;
            case "Email desc":
                students = students.OrderByDescending(s => s.Email);
                break;
            default:
                students = students.OrderBy(s => s.LastName);
                break;
        }
        int pageSize = 4;
        int pageNumber = (page ?? 1);
        return View(students.ToPagedList(pageNumber, pageSize));
    }

И мой взгляд:

@model PagedList.IPagedList<MVCAppName.Models.Student>

@{
    ViewBag.Title = "Students";
}

<h2>Students</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm())
{
    <p>
        Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string) &nbsp;
        <input type="submit" value="Search" /></p>
}
<table>
    <tr>
    <th></th>
    <th>
        @Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
    <th>
        @Html.ActionLink("First Name", "Index", new { sortOrder = ViewBag.FNameSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
    <th>
        @Html.ActionLink("Email", "Index", new { sortOrder = ViewBag.EmailSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
    <th>
        @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm, currentFilter = ViewBag.CurrentFilter })
    </th>
</tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
            @Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.LastName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.FirstMidName)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Email)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.EnrollmentDate)
        </td>
    </tr>
}

</table>

<div>
    Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber)
    of @Model.PageCount
    &nbsp;
    @if (Model.HasPreviousPage)
    {
        @Html.ActionLink("<<", "Index", new { page = 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
        @Html.Raw("&nbsp;");
        @Html.ActionLink("< Prev", "Index", new { page = Model.PageNumber - 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
    }
    else
    {
        @:<<
        @Html.Raw("&nbsp;");
        @:< Prev
    }
    &nbsp;
    @if (Model.HasNextPage)
    {
        @Html.ActionLink("Next >", "Index", new { page = Model.PageNumber + 1, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
        @Html.Raw("&nbsp;");
        @Html.ActionLink(">>", "Index", new { page = Model.PageCount, sortOrder = ViewBag.CurrentSort, currentFilter=ViewBag.CurrentFilter  })
    }
    else
    {
        @:Next >
        @Html.Raw("&nbsp;")
        @:>>
    }
</div>

person Ecnalyr    schedule 05.03.2012    source источник
comment
О «Я читал, что ViewBag - это то, чего следует избегать любой ценой, пытаясь создать правильный проект MVC». При создании любого программного обеспечения следует избегать любых догматических правил любой ценой.   -  person MikeSW    schedule 05.03.2012
comment
мои тирады о viewbag: completedevelopment.blogspot.com / 2011/12 /   -  person Adam Tuliper - MSFT    schedule 05.03.2012
comment
Означает ли это, что если бы были проверки во время компиляции, не было бы отрицательного клейма, связанного с ViewBag?   -  person Ecnalyr    schedule 05.03.2012


Ответы (3)


Контроллер должен обрабатывать сортировку, представление просто отображает данные. Вы уже это делаете, поэтому вам нужно только определить модель представления, которую вы предварительно поместили в ViewBag.

public class ShowStudentsModel
{
    public string CurrentSort {get;set;}
    public string NameSortParm {get;set;}
      //and so on... you create a property for each property set in the ViewBag
    public IEnumerable<Student> Students {get;set;}
}

Тогда в представлении

@model ShowStudentsModel

@foreach(var item in Model.Students)
{
      //html code
  }
person MikeSW    schedule 05.03.2012
comment
зачем вам сортировать в контроллере, а не в представлении? - person northben; 26.03.2013
comment
Сортировка обычно выполняется моделью (не связанной с ViewModel), но контроллер решает, какую модель следует использовать. Вот что я имел в виду, говоря «контроллер должен обрабатывать сортировку». Представление просто отображает данные, разумеется, оно может их сортировать, но только данные, предоставленные Контроллером. - person MikeSW; 27.03.2013
comment
Если бы я хотел предоставить возможность интерактивной сортировки, не было бы проще изменить сортировку в представлении, чем создать новую модель? Я просто спрашиваю, так как я новичок. :) - person northben; 28.03.2013
comment
В новой модели нет необходимости. И под интерактивной сортировкой, я думаю, вы имеете в виду javascript на стороне клиента, но даже тогда вы можете отправить запрос контроллеру, который возвращает (как json) страницу результатов. Представление предназначено только для визуализации модели представления и может сортировать только модель представления. - person MikeSW; 28.03.2013
comment
ах, теперь это имеет смысл. Спасибо! - person northben; 28.03.2013

Я думаю, что лучше всего было бы создать подкласс PagedList.IPagedList<T>, который вы используете, и добавить туда порядок сортировки. Итак, в конце вашего контроллера у вас будет следующее:

    return View(students.ToPagedList(pageNumber, pageSize, sortOrder));

Но если вы не желаете этого делать, вы можете просто создать новый класс ViewModel для хранения PagedList (вашей текущей модели), а также дополнительных данных, которые вам нужны (то есть вашего порядка сортировки).

    return View(new SortedStudents
    {
        Students = students.ToPagedList(pageNumber, pageSize);
        SortOrder = sortOrder
    });

С SortedStudents, определенным следующим образом:

public class SortedStudents
{
    public PagedList.IPagedList<MVCAppName.Models.Student> Students { get; set; }
    public string SortOrder { get; set; }
}
person Clafou    schedule 05.03.2012

Вы можете сделать обертку вокруг своего класса учеников

public class StudentWrapper
{

List<Students> studentList { get; set; }
String currentSort { get; set; }

public StudentWrapper() {

studentlist = new List<Students>();

}

В вашем контроллере вы должны создать новый StudentWrapper

StudentWrapper sw = new StudentWrapper();

и установите список студентов:

sw.studentList = db.Students.ToList();

и sortOrder

sw.currentSort = SortOder;

Вы передаете эту модель своему представлению

return View(sw);

в вашем представлении вы должны использовать StudentWrapper

@model List<MVCAppName.Models.StudentWrapper>

Я не знаю, как работает ваш пейджинг, так что вам придется это выяснить.

Но я тоже не вижу проблем с использованием ViewBag.

person Kenci    schedule 05.03.2012