如何在 MVC 中对相关数据的集合进行排序?
How do I sort collection of related data in MVC?
我是MVC的新手,所以请温柔点。
这是我的数据设置方式;
我有一份订单清单 table。然后每个订单都有一个步骤列表,用于在不同的 table 中完成该订单,这些 table 具有一对多关系。
在我的订单索引页面上,我列出了所有有效订单。在详细信息页面上,我想列出该订单的每个步骤。我 运行 遇到的问题是步骤并不总是按 table 顺序排列。每个步骤都有一个步骤编号,用于确定它在步骤序列中的位置,但它不是键值。
我想我遗漏了一些简单的东西。
在此先感谢您的帮助。
这是我正在使用的东西;
OR_ORDER.cs
public partial class OR_ORDER
{
public OR_ORDER()
{
this.OR_OP = new HashSet<OR_OP>();
}
public decimal NO { get; set; }
public Nullable<decimal> MUSTERNO { get; set; }
public Nullable<decimal> PRONO { get; set; }
public Nullable<decimal> GRPNO { get; set; }
public string NAME { get; set; }
public string DESCR { get; set; }
public string IDENT { get; set; }
public Nullable<decimal> PPARTS { get; set; }
public Nullable<decimal> SEQNO { get; set; }
public Nullable<decimal> SOURCE { get; set; }
public decimal STATUS { get; set; }
public virtual ICollection<OR_OP> OR_OP { get; set; }
}
OR_OP.cs
public partial class OR_OP
{
public decimal NO { get; set; }
public decimal ORNO { get; set; }
public string NAME { get; set; }
public string DESCR { get; set; }
public Nullable<decimal> APARTS { get; set; }
public Nullable<decimal> ATE { get; set; }
public Nullable<decimal> ATOTAL { get; set; }
public Nullable<decimal> AWPLACE { get; set; }
public Nullable<decimal> BPARTS { get; set; }
public decimal CFLAG { get; set; }
public Nullable<System.DateTime> CHTIME { get; set; }
public Nullable<decimal> CPCOST { get; set; }
}
控制器中的详细操作
public ActionResult Details(decimal id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
OR_ORDER or_order = db.OR_ORDER.Find(id);
//or_order.OR_OP = or_order.OR_OP.OrderBy(s => s.NAME);
if (or_order == null)
{
return HttpNotFound();
}
return View(or_order);
}
终于Detials.cshtml查看
@model Scheduler.Models.OR_ORDER
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>JOB: @Model.NAME</h4>
<h4>@Model.DESCR</h4>
<hr />
<table>
<tr>
<th width="150px">
Description
</th>
<th width ="150px">
Time
</th>
<th width="150px">
Op Info
</th>
</tr>
@foreach (var item in Model.OR_OP)
{
<tr>
<td width="150px">
@Html.DisplayFor(modelitem => item.DESCR)
</td>
<td width="150px">
@Html.DisplayFor(modelitem => item.PTE)
</td>
<td width="150px">
@Html.DisplayFor(modelitem => item.OPINFO)
</td>
</tr>
}
</table>
</div>
<p>
@*Html.ActionLink("Edit", "Edit", new { id = Model.NO }) |*@
@Html.ActionLink("Back Job List", "Index")
</p>
根据您提供给我们的代码,最简单(侵入性最小)的方法是向模型添加 LINQ 表达式:
@foreach (var item in Model.OR_OP.OrderBy(c => c.NO)) // I wasn't sure what
// property to use
但是,在您的模型中使用该逻辑有点混乱。
您还可以将 属性 类型从 HashSet<OR_PP>
调整为 List<OR_OP>
,如果您有 IComparer
,则调用 List<>.Sort()
,或者只使用什么您已经使用与我所做的相同的 LINQ 调用进行了注释,并且 .ToList()
它。
最好的解决方案,尽管不是非常漂亮,可能是添加另一个 class,它有一个额外的 属性 用于这个排序的集合。您将在两个 class 之间拥有冗余数据,但这是所有选项中最易于维护的。
您的评论行的问题是 HashSet<>
未排序。它们必须在实践中,因为 RAM 是,因此它们每次都会以相同的顺序枚举,但理论上,任何顺序都不能保证。但是,List<>
确实支持排序,它还实现了 ICollection<T>
,这是 EF 集合属性的要求。
在你发表评论后,听起来你有一个更基本的问题——听起来你的操作是以字符串形式存储的。
快速解决这个问题的方法是按解析排序。
@foreach (var item in Model.OR_OP.OrderBy(c => int.Parse(c.NO))) // I wasn't sure what
// property to use
那是糟糕。不要这样做,除非你有,即使你有,也使用视图模型。
真的,您应该更改数据库和 属性 的类型。
这就是所谓的"Learning a lesson the hard way"。您看到的大多数示例代码只是将您的实体从 entity framework 直接发送到视图。这个问题很多。
首先,正如您所发现的那样,您无法通过这种方式真正控制实体模型的其他部分。例如,您不能轻易地告诉模型对导航 属性 进行排序。至少在这一点上不是。
其次,您还坚持将视图与数据模型紧密耦合,并且您的视图可能不具有完全相同的结构。
第三,您的视图和数据模型之间可能存在安全或验证差异。
最终,你想要的是一个视图模型。这是一个根据视图的需要量身定制的模型,仅包含视图所需的数据。您现在可以以任何格式、结构、顺序或任何您想要的方式将数据复制到该 ViewModel。
另一种方法是在视图中执行排序逻辑,这对某些人来说可能是可以接受的。我不这么认为。
我是MVC的新手,所以请温柔点。
这是我的数据设置方式; 我有一份订单清单 table。然后每个订单都有一个步骤列表,用于在不同的 table 中完成该订单,这些 table 具有一对多关系。
在我的订单索引页面上,我列出了所有有效订单。在详细信息页面上,我想列出该订单的每个步骤。我 运行 遇到的问题是步骤并不总是按 table 顺序排列。每个步骤都有一个步骤编号,用于确定它在步骤序列中的位置,但它不是键值。
我想我遗漏了一些简单的东西。
在此先感谢您的帮助。
这是我正在使用的东西;
OR_ORDER.cs
public partial class OR_ORDER
{
public OR_ORDER()
{
this.OR_OP = new HashSet<OR_OP>();
}
public decimal NO { get; set; }
public Nullable<decimal> MUSTERNO { get; set; }
public Nullable<decimal> PRONO { get; set; }
public Nullable<decimal> GRPNO { get; set; }
public string NAME { get; set; }
public string DESCR { get; set; }
public string IDENT { get; set; }
public Nullable<decimal> PPARTS { get; set; }
public Nullable<decimal> SEQNO { get; set; }
public Nullable<decimal> SOURCE { get; set; }
public decimal STATUS { get; set; }
public virtual ICollection<OR_OP> OR_OP { get; set; }
}
OR_OP.cs
public partial class OR_OP
{
public decimal NO { get; set; }
public decimal ORNO { get; set; }
public string NAME { get; set; }
public string DESCR { get; set; }
public Nullable<decimal> APARTS { get; set; }
public Nullable<decimal> ATE { get; set; }
public Nullable<decimal> ATOTAL { get; set; }
public Nullable<decimal> AWPLACE { get; set; }
public Nullable<decimal> BPARTS { get; set; }
public decimal CFLAG { get; set; }
public Nullable<System.DateTime> CHTIME { get; set; }
public Nullable<decimal> CPCOST { get; set; }
}
控制器中的详细操作
public ActionResult Details(decimal id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
OR_ORDER or_order = db.OR_ORDER.Find(id);
//or_order.OR_OP = or_order.OR_OP.OrderBy(s => s.NAME);
if (or_order == null)
{
return HttpNotFound();
}
return View(or_order);
}
终于Detials.cshtml查看
@model Scheduler.Models.OR_ORDER
@{
ViewBag.Title = "Details";
}
<h2>Details</h2>
<div>
<h4>JOB: @Model.NAME</h4>
<h4>@Model.DESCR</h4>
<hr />
<table>
<tr>
<th width="150px">
Description
</th>
<th width ="150px">
Time
</th>
<th width="150px">
Op Info
</th>
</tr>
@foreach (var item in Model.OR_OP)
{
<tr>
<td width="150px">
@Html.DisplayFor(modelitem => item.DESCR)
</td>
<td width="150px">
@Html.DisplayFor(modelitem => item.PTE)
</td>
<td width="150px">
@Html.DisplayFor(modelitem => item.OPINFO)
</td>
</tr>
}
</table>
</div>
<p>
@*Html.ActionLink("Edit", "Edit", new { id = Model.NO }) |*@
@Html.ActionLink("Back Job List", "Index")
</p>
根据您提供给我们的代码,最简单(侵入性最小)的方法是向模型添加 LINQ 表达式:
@foreach (var item in Model.OR_OP.OrderBy(c => c.NO)) // I wasn't sure what
// property to use
但是,在您的模型中使用该逻辑有点混乱。
您还可以将 属性 类型从 HashSet<OR_PP>
调整为 List<OR_OP>
,如果您有 IComparer
,则调用 List<>.Sort()
,或者只使用什么您已经使用与我所做的相同的 LINQ 调用进行了注释,并且 .ToList()
它。
最好的解决方案,尽管不是非常漂亮,可能是添加另一个 class,它有一个额外的 属性 用于这个排序的集合。您将在两个 class 之间拥有冗余数据,但这是所有选项中最易于维护的。
您的评论行的问题是 HashSet<>
未排序。它们必须在实践中,因为 RAM 是,因此它们每次都会以相同的顺序枚举,但理论上,任何顺序都不能保证。但是,List<>
确实支持排序,它还实现了 ICollection<T>
,这是 EF 集合属性的要求。
在你发表评论后,听起来你有一个更基本的问题——听起来你的操作是以字符串形式存储的。
快速解决这个问题的方法是按解析排序。
@foreach (var item in Model.OR_OP.OrderBy(c => int.Parse(c.NO))) // I wasn't sure what
// property to use
那是糟糕。不要这样做,除非你有,即使你有,也使用视图模型。
真的,您应该更改数据库和 属性 的类型。
这就是所谓的"Learning a lesson the hard way"。您看到的大多数示例代码只是将您的实体从 entity framework 直接发送到视图。这个问题很多。
首先,正如您所发现的那样,您无法通过这种方式真正控制实体模型的其他部分。例如,您不能轻易地告诉模型对导航 属性 进行排序。至少在这一点上不是。
其次,您还坚持将视图与数据模型紧密耦合,并且您的视图可能不具有完全相同的结构。
第三,您的视图和数据模型之间可能存在安全或验证差异。
最终,你想要的是一个视图模型。这是一个根据视图的需要量身定制的模型,仅包含视图所需的数据。您现在可以以任何格式、结构、顺序或任何您想要的方式将数据复制到该 ViewModel。
另一种方法是在视图中执行排序逻辑,这对某些人来说可能是可以接受的。我不这么认为。