Данные Foreach ViewBag дают "объект" не содержит определения для "Var"

У меня есть контроллер, который строит запрос от Linq к Sql для передачи в объект ViewBag.products. Проблема в том, что я не могу использовать цикл foreach, как я ожидал.

Вот код в контроллере, строящем запрос с примененной функцией.ToList().

var products = from bundles in db.Bundle
    join bProducts in db.BundleProducts on bundles.bundleId equals bProducts.bundleID
    join product in db.Products on bProducts.productID equals product.productID
    join images in db.Images on product.productID equals images.productID
    where bundles.bundleInactiveDate > DateTime.Now
          select new {
              product.productName,
              product.productExcerpt,
              images.imageID,
              images.imageURL
           };
ViewBag.products = products.ToList();

Поскольку я использую другую модель в Index.cshtml для других необходимых элементов, я подумал, что можно использовать простой Html.Partial для включения цикла viewbag. Я пробовал это с тем же результатом с использованием и без использования частичного и просто с помощью foreach в index.cshtml. Фрагмент, который включает в себя частичное ниже:

<div id="bundle_products">
<!--build out individual product icons/descriptions here--->
@Html.Partial("_homeBundle")
</div>

В моем файле _homeBundle.cshtml у меня есть следующее:

@foreach (var item in ViewBag.products)
{ 
    @item
}

Я получаю данные ViewBag, но я получаю весь список как вывод:

{ productName = Awesomenes Game, productExcerpt = <b>Awesome game dude!</b>, imageID = 13, imageURL = HotWallpapers.me - 008.jpg }{ productName = RPG Strategy Game, productExcerpt = <i>Test product excerpt</i>, imageID = 14, imageURL = HotWallpapers.me - 014.jpg }

То, что я думал, что мог сделать, было:

@foreach(var item in ViewBag.Products)
{
    @item.productName
}

Как видите, в выводе productName = Awesomenes Game. Тем не менее, я получаю сообщение об ошибке "объект" не содержит определение для "productName", когда я пытаюсь это сделать.

Как я могу вывести каждое "поле", так сказать, индивидуально в моем цикле, чтобы я мог применить надлежащие HTML-теги и стили, необходимые для моей страницы?

Нужно ли мне сделать совершенно новую ViewModel, чтобы сделать это, а затем создать шаблон отображения, как указано здесь: "объект" не содержит определения для "X"

Или я могу сделать то, что я пытаюсь здесь?

*****ОБНОВИТЬ*****

В моем контроллере у меня теперь есть следующее:

        var bundle = db.Bundle.Where(a => a.bundleInactiveDate > DateTime.Now);
        var products = from bundles in db.Bundle
                       join bProducts in db.BundleProducts on bundles.bundleId equals bProducts.bundleID
                       join product in db.Products on bProducts.productID equals product.productID
                       join images in db.Images on product.productID equals images.productID
                       where bundles.bundleInactiveDate > DateTime.Now
                       select new {
                           product.productName,
                           product.productExcerpt,
                           images.imageID,
                           images.imageURL
                       };
        var bundleContainer = new FullBundleModel();

        bundleContainer.bundleItems = bundle;

        return View(bundleContainer);

У меня есть модель, FullBundleModel

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace JustBundleIt.Models
{
    public class FullBundleModel
    {
        public IQueryable<Bundles> bundleItems { get; set; }
        public IQueryable<Images> imageItems { get; set; }
    }
}

и мой взгляд теперь имеет

@model IEnumerable<JustBundleIt.Models.FullBundleModel>

@foreach (var item in Model) 
{
<div class="hp_bundle">

    <h3>@Html.Display(item.bundleName)</h3>
    </div>
}       

Если я удаляю IEnumerable из ссылки на модель, foreach выдает ошибку, что нет общедоступного определения для перечислителя.

В @Html.Display(item.bundleName) выдает ошибку, что модель не имеет определения для bundleName. Если я попытаюсь

@foreach(var item in Model.bundleItems)

Я получаю сообщение об ошибке, что bundleItems не определен в модели.

Так что я не правильно подключил, чтобы использовать комбинированную модель?

2 ответа

Решение

Почему бы не создать новую модель, которая содержит все данные, которые вам нужны?

Пример первый:

public class ContainerModel
{
    public IQueryable<T> modelOne;
    public IQueryable<T> modelTwo;
}

Это позволит вам получить доступ к любому из ваших запросов в Razor:

@model SomeNamespace.ContainerModel

@foreach (var item in Model.modelOne)
{
    //Do stuff
}

Лично я вообще не использую ViewBag и храню все, что мне нужно, в таких моделях, потому что он НЕ динамический и заставляет все быть строго типизированными. Я также считаю, что это дает вам четко определенную структуру / намерение.

И просто для ясности:

public ViewResult Index()
{
    var queryOne = from p in db.tableOne
                   select p;

    var queryTwo = from p in db.tableTwo
                   select p;

    var containerModel = new ContainerModel();
    containerModel.modelOne = queryOne;
    containerModel.modelTwo = queryTwo;

    return View(containerModel);
}

Пример второй:

public class ContainerModel
{
    [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")] //Format: MM/dd/yyyy (Date)
    public Nullable<DateTime> startDate { get; set; }
    [DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}")] //Format: MM/dd/yyyy (Date)
    public Nullable<DateTime> endDate { get; set; }
    public SelectList dropdown { get; set; }
    public IQueryable<T> modelOne { get; set; }
    public IQueryable<T> modelTwo { get; set; }
}

В этом случае вы сохранили 3 других элемента в модели с двумя запросами. Вы можете использовать Html-помощники для создания выпадающего списка в Razor:

@Html.DropDownList("dropdown", Model.dropdown)

И вы можете использовать помощник DisplayFor для отображения ваших дат, как определено в вашей модели с аннотациями данных:

@Html.DisplayFor(a => a.startDate)

Это выгодно для IMO, поскольку позволяет вам определять все данные, которые вы хотите использовать в вашем представлении, и то, как вы планируете форматировать эти данные в одном месте. Ваш Контроллер содержит всю бизнес-логику, ваша Модель содержит все данные / форматирование, а ваш Просмотр касается только содержимого вашей страницы.

Нужно ли мне сделать совершенно новую ViewModel для этого, а затем создать шаблон отображения, как указано здесь...

Ответ Дарина, который вы связали, гласит важную концепцию: анонимные типы не предназначены для использования через границы сборки и недоступны для Razor. Я бы добавил, что редко бывает полезно выставлять анонимные объекты вне их непосредственного контекста.

Создание модели представления специально для потребления представления - почти всегда правильный подход. Модели представлений можно повторно использовать в представлениях, если вы предоставляете аналогичные данные.

Нет необходимости создавать шаблон отображения, но это может быть полезно, если вы хотите повторно использовать логику отображения. Помощники по HTML также могут выполнять аналогичную функцию, обеспечивая логику отображения многократного использования.

Сказав все это, невозможно передать анонимный тип представлению или прочитать члены анонимного типа. RouteValueDictionary может принимать анонимный тип (даже в разных сборках) и читать его свойства. Отражение позволяет читать участников независимо от видимости. Хотя это имеет свои применения, передача данных в представление не является одним из них.

Больше чтения:

Другие вопросы по тегам