Clint Clark
Clint Clark

Reputation: 173

How do I sort collection of related data in MVC?

I am new to MVC, so please be gentle.

Here is how my data is set up; I have a list of Orders in one table. Each Order then has a list of steps to complete that order in a different table, these tables have a one-to-many relationship.

On my order index page I list all of my active orders. On the detail page I want to list each step for that order. The problem that I am running into is that steps are not always in the table in order. Each step does have a step number that determines where in the sequence of steps it occurs, but it isn't the key value.

I imagine that I am missing something simple.

Thanks in advance for any help.

Here is what I am working with;

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; }
}

Details Action in Controller

    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);
    }

Finally Detials.cshtml View

@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>

Upvotes: 0

Views: 3008

Answers (2)

Matthew Haugen
Matthew Haugen

Reputation: 13286

The simplest (least invasive) approach from the code you've given us would be to add a LINQ expression to the model:

@foreach (var item in Model.OR_OP.OrderBy(c => c.NO)) // I wasn't sure what
                                                      // property to use

However, having that logic in your model is a little messy.

You could also adjust the property type from HashSet<OR_PP> to List<OR_OP>, then call List<>.Sort() if you have an IComparer, or just use what you already have commented out, with the same LINQ call as I've made, and .ToList() it.

The best solution, albeit not super pretty, might be to add another class that has an extra property for this sorted collection. You'll have redundant data between the two classes, but that will be the most maintainable of any option.

The issue with your commented line is that HashSet<>s aren't ordered. They have to be in practice, because RAM is, and they'll enumerate in the same order every time because of that, but in theory, no order is guaranteed in any way. List<>, however, does support ordering, and it also implements ICollection<T>, which is the requirement for EF collection properties.


After your comment, it sounds like you have a more fundamental issue--it sounds like your operations are stored as strings.

The quick-fix for that, and I absolutely hate this, is to sort by the parse.

@foreach (var item in Model.OR_OP.OrderBy(c => int.Parse(c.NO))) // I wasn't sure what
                                                                 // property to use

That's awful. Don't do it unless you have to, and even if you do, use a view model.

Really, you should change the type in your database and of your property.

Upvotes: 0

Erik Funkenbusch
Erik Funkenbusch

Reputation: 93444

This is what's called "Learning a lesson the hard way". Most sample code you see just sends your entities from entity framework directly to the view. There are many problems with this.

First, you have no real control over other parts of the entity model this way, as you've found out. You can't easily tell the model to sort a navigational property, for instance. At least not at this point.

Second, You are also stuck tightly coupling your view to your data model, and your view may not have exactly the same structure.

Third, there may be security or validation differences between your view and the data model.

Ultimately, what you want is a View Model. This is a model that is tailored to the needs of the view, with just the data the view needs. You can now copy data to that ViewModel in any format, structure, order, or whatever you want.

The alternative is to do your sorting logic in the view, which may be acceptable to some people. I don't think so.

Upvotes: 2

Related Questions