简体   繁体   中英

How to convert List<IEnumerable<ProductViewModel>> into <IEnumerable<ProductViewModel>>?

I am accessing data from a Category's ICollection<Product> Products property. If you look at my controller code below, using the second select, I change each product's Form from a Product entity into a ProductViewModel which returns an IQueryable<IEnumerable<ProductViewModel>> .

Finally, with the ToListAsync() method, the model returns a List<IEnumerable<ProductViewModel>> which is not accepted by my View .

My controllers model is this:

var model = (await db.Categories.Where(c => c.Id == id).Select(p => p.Products.Select(x => new 
                    ProductViewModel { Id = x.Id, Name = x.Name,  ByteImage = x.Image, Price = x.Price})).ToListAsync());

            return View(model);

The return type for this model is List<IEnumerable<ProductViewModel>> and I cannot iterate through this model inside my View to display the data.

View model given to the View is:

@model IEnumerable<ProductViewModel> .

This is the error message I get:

Compiler Error Message: CS0411: The type arguments for method 'DisplayNameExtensions.DisplayNameFor(HtmlHelper, Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Source Error:

Line 14:     <tr>
Line 15:         <th>
Line 16:             @Html.DisplayNameFor(model => model.Name)
Line 17:             
Line 18:         </th>

ProductViewModel:

public class ProductViewModel
    {
        public int Id { get; set; }
        [Required, Display(Name="Product Name")]
        public string Name { get; set; }
        [Required, DataType(DataType.Upload)]
        public HttpPostedFileBase Image { get; set; }
        public string OutputImage { get; set; }
        public Byte[] ByteImage { get; set; }
        [Required]
        public Decimal Price { get; set; }

        public static byte[] ConvertToByte(ProductViewModel model)
        {
            if (model.Image != null)
            {
                byte[] imageByte = null;
                BinaryReader rdr = new BinaryReader(model.Image.InputStream);
                imageByte = rdr.ReadBytes((int)model.Image.ContentLength);

                return imageByte;
            }

            return null;
        }

        // ViewModel => Model | Implicit type Operator
        public static implicit operator Product(ProductViewModel viewModel)
        {
            var model = new Product
            {
                Id = viewModel.Id,
                Name = viewModel.Name,
                Image = ConvertToByte(viewModel),
                Price = viewModel.Price
            };

            return model;
        }

        // Model => ViewModel | Implicit type Operator
        public static implicit operator ProductViewModel(Product model)
        {
            var viewModel = new ProductViewModel
            {
                Id = model.Id,
                Name = model.Name,
                OutputImage = string.Format("data:image/jpg;base64,{0}", Convert.ToBase64String(model.Image)),
                Price = model.Price
            };

            return viewModel;
        }

    }

This is the View Index:

@model List<ValueVille.Models.ProductViewModel>
@using MvcApplication8.Models

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)

        </th>
        <th>
            @Html.DisplayNameFor(model => model.Image)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Price)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Image)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Price)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}

</table>

You need to use SelectMany instead:

(await db.Categories
         .Where(c => c.Id == id)
         .SelectMany(p => p.Products.Select(x => new ProductViewModel { Id = x.Id, Name = x.Name, ByteImage = x.Image, Price = x.Price }))
         .ToListAsync());

SelectMany is useful when you want to project from multiple sequences into a single (flat) sequence.


As per your update:

Since your model is actually List<ProductViewModel> , when you want to display the name of certain property, you need to point to an actual ProductViewModel instance within that list.

For example:

@Html.DisplayNameFor(model => model[0].Name)

See MSDN

At the moment, you sequence looks like this

[ [model1, model2, model3], [model4, model] ]

to flatten it you could

model.SelectMany(m => m)

for

[ model1, model2, model3, model4, model5 ]

(if that's really what you want)

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