简体   繁体   中英

Trying to add a ICollection to a ViewModel in ASP.NET

I have had a look at this post and the chap there seems to want to do the same thing that I am aiming for. I have inherited this application and although I am a .NET developer the MVC stuff seems magical in comparison to other MVC frameworks in other languages which I prefer. My salient code is as follows:

public class PurchaseOrderViewModel
{
    public PurchaseOrderViewModel()
    {
        this.PurchaseOrder = new PurchaseOrder();
    }
    // Some other fields here....
    public PurchaseOrder PurchaseOrder { get; set; }

    public IList<PurchaseOrderItem> OrderItems { get; set; }
 }

The controller:

    [HttpPost]
    public ActionResult Details(PurchaseOrderViewModel viewModel)
    {
       // bunch of stuff. the viewModels.OrderItems here is null. 
    }

The OrderItems in the model is a ICollection<> I am converting to an IList for the iterative nature. The relevant part of the view is as follows:

 @model PurchaseOrderViewModel
 @for (var i = 0; i < this.Model.OrderItems.Count; i++)
 {                
    @Html.EditorFor(m => m.OrderItems[i].Quantity);
 }

So why am I not getting the OrderItems coming through? Is this to do with the fact that they are IList rather than ICollection? If so, what is the workaround?

EDIT: I have changed the view code as I left something in that I was trying before.

EDIT 2: I took into consideration current comment/answers so now I have the following changes made:

viewModel:

    public ICollection<PurchaseOrderItem> PurchaseOrderItems { get; set; }

PurchaseOrderItem.cshtml: (I have left this actually as it is in code just in case):

@model Downland.Model.PurchaseOrderItem

<tr style="background-color: #fff; padding: 8px;">
<td style="text-align: left; width: 210px;">
    @if (this.Model.ProductID == null)
    {
        <img class="viewproduct" src="~/Images/GetThumbnail/-1" alt="Product Image" style="padding: 8px;" />
    }
    else
    {
        <img class="viewproduct" src="~/Images/GetProductImage?productId=@(this.Model.ProductID)" alt="Product Image" style="padding: 8px;" />
    }
</td>
<td>
    @(string.IsNullOrEmpty(this.Model.SKU) ? this.Model.NonDownlandSKU : this.Model.SKU)
</td>
<td>@this.Model.ProductName</td>
<td>
    @this.Model.Quantity
    @Html.EditorFor(m => m.Quantity)
</td>
<td>
    @string.Format("{0:C}", this.Model.PriceExVAT)
    @Html.EditorFor(m => m.PriceExVAT)
</td>
<td>@string.Format("{0:C}", this.Model.VATTotal)</td>
<td>
    @string.Format("{0:C}", this.Model.ItemTotal)
</td>
</tr>

View code:

@Html.EditorFor(m => m.PurchaseOrderItems);

Just another interesting point. On the GET I set the PurchaseOrder and then the PurchaseOrderItems in the view model based on the database data. On the Post it appears that we are setting the purchaseOrder in the view model with data from the database again. There are some other fields that are being set correctly in the view model based on what is passed to the post but the PurchaseOrderItems is still null.

You should not be doing this conversion in the view because the following expression simply is too complex and not supported by the standard editor templates:

m => m.PurchaseOrder.PurchaseOrderItems.ToList()[i].Quantity

So, you should obviously use a real view model in which you don't just stuff your EF objects as properties and naming it with the ViewModel suffix (which is what this PurchaseOrderViewModel look like). So once you have a real view model you can easily access the corresponding property because it will be of type IList<T> :

@Html.EditorFor(m => m.PurchaseOrder.PurchaseOrderItems[i].Quantity);

This being said, if for some reason you have inherited some legacy crap like that there's still a workaround for you. You can use editor templates.

So you start by adding the following template in ~/Shared/EditorTemplates/PurchaseOrderItem.cshtml :

@model PurchaseOrderItem
@Html.EditorFor(x => x.Quantity)

The name of this template is very important because it all works by convention. It should be named as the type of the ICollection<> that you have and located in ~/Shared/EditorTemplates .

And then simply adapt your view like that:

@Html.EditorFor(m => m.PurchaseOrder.PurchaseOrderItems);

The EditorFor helper in this case will infer that PurchaseOrderItems is an ICollection<T> and it will automatically loop through the elements of this collection and render the corresponding ~/Shared/EditorTemplates/T.cshtml template that you could customize.


UPDATE:

It looks like I need to post a simple example to illustrate how editor templates can be used.

As always let's start with the view model(s):

public class PurchaseOrderViewModel
{
    public ICollection<PurchaseOrderItem> PurchaseOrderItems { get; set; }
}

public class PurchaseOrderItem
{
    public int Quantity { get; set; }
}

then a controller with 2 actions (one for displaying a form and one for handling the submitted data from this form):

public ActionResult Index()
{
    var model = new PurchaseOrderViewModel();
    // This information will probably come from querying some data store. 
    // That could be a SQL database for example. But for the purpose
    // of this sample we are just hardcoding some values to illustrate 
    // the concept without any dependencies
    model.PurchaseOrderItems = new[]
    {
        new PurchaseOrderItem
        {
            Quantity = 5,
        }
    };
    return View(model);
}

[HttpPost]
public ActionResult Index(PurchaseOrderViewModel model)
{
    // ... everything gets bound correctly here
}

then a strongly typed view:

@model PurchaseOrderViewModel
@using (Html.BeginForm())
{
    @Html.EditorFor(x => x.PurchaseOrderItems)
    <input type="submit" value="OK" />
}

and finally the corresponding editor template ( ~/Shared/EditorTemapltes/PurchaseOrderItem.cshtml ):

@model PurchaseOrderItem
@Html.EditorFor(x => x.Quantity)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM