【问题标题】:Foreach ViewBag data gives 'object' does not contain a definition for 'var'Foreach ViewBag 数据给出 'object' 不包含 'var' 的定义
【发布时间】:2012-09-10 06:32:10
【问题描述】:

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

<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。但是,当我尝试此操作时,我收到错误“对象”不包含“产品名称”的定义。

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

我是否需要制作一个全新的 ViewModel 来执行此操作,然后创建一个显示模板,如下所示:'object' does not contain a definition for '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。

那么我没有正确连接以使用组合模型?

【问题讨论】:

  • 第一:不要将您的模型定义为 IEnumerable。只需使用@model JustBundleIt.Models.FullBundleModel。第二:将 for 循环更改为 @foreach(var item in Model.bundleItems),将 HTML 助手更改为 @Html.DisplayFor(modelItem =&gt; item.bundleName)
  • 谢谢。这就是我可以说我今天已经坚持了太久的方式。非常感谢。
  • 大声笑,没问题。我们都去过那里。在我们的世界上做事的方式通常不止一种,这无济于事。您可能可以通过 100 多种不同的方式来解决这个问题,但最终还是会得到相同的结果。

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


【解决方案1】:

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

您链接的Darin's answer 说明了重要概念:Anonymous types are not intended for use across assembly boundaries 并且对 Razor 不可用。我要补充一点,在其直接上下文之外公开匿名对象很少是一个好主意。

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

不必创建显示模板,但如果您想重用显示逻辑,它会很有用。 HTML 助手也可以填充类似的功能,提供可重用的显示逻辑。

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

更多阅读:

【讨论】:

    【解决方案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; }
    }
    

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

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

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

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

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

    【讨论】:

    • 那么一个模型除了包含其他模型什么都不做,那么我可以毫无问题地访问视图中的多个模型吗?
    • @SouthPlatte 我不会这么说的。您的模型可以包含您想要的任何内容。我把我需要的一切都放在模型中的特定视图中。稍后我将添加一个示例。
    • @SouthPlatte 添加了第二个示例,其中包含多个查询。
    • 谢谢。还是有一些问题。我可能会更新我所做的以澄清我对您的示例的尝试。
    猜你喜欢
    • 1970-01-01
    • 2018-05-28
    • 1970-01-01
    • 2014-09-09
    • 2018-03-30
    • 2016-01-24
    • 1970-01-01
    • 2012-06-21
    • 2021-11-29
    相关资源
    最近更新 更多