Foreach ViewBag数据给出'对象'不包含'var'的定义

时间:2012-09-18 02:11:07

标签: asp.net-mvc-3 razor viewbag

我有一个控制器正在构建一个从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循环。我已尝试使用和不使用partial的相同结果,只需使用index.cshtml中的foreach。包含部分的片段如下:

<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。但是,当我尝试这个时,我得到错误'object'不包含'productName'的定义。

如何输出每个“字段”以便在循环中单独说出,以便我可以为我的页面应用正确的HTML标记和样式?

我是否需要创建一个全新的ViewModel才能执行此操作,然后创建一个显示模板,如下所示:'object' does not contain a definition for 'X'

或者我可以做我在这里尝试的事情吗?

***** UPDATE *****

在我的控制器中,我现在有以下内容:

        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 个答案:

答案 0 :(得分:2)

  

我是否需要创建一个全新的ViewModel才能执行此操作,然后创建一个   这里引用的显示模板...

您链接的

Darin's answer说明了重要的概念:Anonymous types are not intended for use across assembly boundaries并且Razor无法使用。我想补充一点,在他们的直接上下文之外暴露匿名对象并不是一个好主意。

专门为视图消耗创建视图模型几乎总是正确的方法。如果您要呈现类似的数据,则可以跨视图重复使用视图模型。

没有必要创建显示模板,但如果要重用显示逻辑,它可能很有用。 HTML帮助程序也可以填充提供可重用显示逻辑的类似功能。

说完所有这些之后,将匿名类型传递给视图或读取匿名类型的成员并非不可能。 RouteValueDictionary可以采用匿名类型(甚至跨程序集)并读取其属性。无论可见性如何,反射都可以读取成员。虽然这有其用途,但将数据传递给View是而不是其中之一。

更多阅读:

答案 1 :(得分:0)

为什么不创建包含您需要的所有数据的新模型?

示例一:

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

在这种情况下,您已使用2个查询在模型中存储了3个其他项目。您可以使用Html帮助程序在Razor中创建下拉列表:

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

您可以使用DisplayFor帮助程序显示模型中使用数据注释定义的日期:

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

这是有利的IMO,因为它允许您定义要在视图中使用的所有数据以及您计划如何在单个位置格式化该数据。您的控制器包含所有业务逻辑,您的模型包含所有数据/格式,而您的视图仅关注页面内容。