【问题标题】:ASP.Net MVC 3, Using ViewModel, error that ViewModel is not IEnumerableASP.Net MVC 3,使用 ViewModel,ViewModel 不是 IEnumerable 的错误
【发布时间】:2012-07-31 02:56:28
【问题描述】:

好的,在这里,谷歌和其他编程网站搜索和搜索之后,是时候问我的第一个问题了。

我有一个视图 Index.cshtml,其中需要两个模型,因此我创建了一个 ViewModel ImageViewModel.cs、两个子模型 ImageModel.cs 和 ProductModel.cs,并有一个控制器 ImageController.cs。

在我的一生中,我无法让 IEnumerable 工作,我应该得到一组数据,一个可能有一个项目(产品),另一个可能有很多(图像)。图片与 productID 相关联。

玩过之后,我现在到了一个点,在视图中,@Html.DisplayFor(modelItem => item.imageTitle) 会抛出 imageTitle 不存在的错误。

如果我恢复散布在各处的众多 IEnumerable 项目中的任何一个,我会继续得到传入的 get ImageViewModel 不是 IEnumerable 并且视图期望它是。

关于它是什么,这里是完整的代码清单:

索引.cshtml

@model IEnumerable<JustAdminIt.Areas.JBI.ViewModels.ImageViewModel>

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
    <th>
        Image Title
    </th>
    <th>
        Image Excerpt
    </th>
    <th>
        Image Description
    </th>
    <th>
        Image 
    </th>
    <th>
       Product ID
    </th>
    <th></th>
</tr>
@foreach (var item in Model) {

<tr>
    <td>
        @Html.DisplayFor(modelItem => item.Image.imageTitle)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Image.imageExcerpt)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Image.imageDescription)
    </td>
    <td>
        <img src="@Url.Content("~/Content/images/product/")@Html.DisplayFor(modelItem => item.Image.imageURL)" alt="Product Image" width="150" />
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Product.productID)
    </td>

    <td>
        @Html.ActionLink("Edit", "Edit", new { id = item.Image.imageID }) |
        @Html.ActionLink("Details", "Details", new { id = item.Image.imageID }) |
        @Html.ActionLink("Delete", "Delete", new { id = item.Image.imageID })
    </td>
</tr>
}

</table>

ProductModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Web.Mvc;

namespace JustAdminIt.Areas.JBI.Models
{
public class Product
{
    [Key]
    public int productID { get; set; }

    [Required]
    [DisplayName("Product Name")]
    public string productName { get; set; }

    [Required]
    [AllowHtml]
    [DisplayName("Product Excerpt")]
    public string productExcerpt { get; set; }

    [Required]
    [AllowHtml]
    [DisplayName("Product Description")]
    public string productDescription { get; set; }

    [DisplayName("Mark As Active")]
    public bool productActive { get; set; }

    [DisplayName("Product Add Date")]
    public DateTime? productAddDate { get; set; }

    [DisplayName("Product Inactive Date")]
    public DateTime? productInactiveDate { get; set; }

}
}

ImageModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Web.Mvc;

namespace JustAdminIt.Areas.JBI.Models
{
public class Image
{
    [Key]
    public int imageID { get; set; }

    [Required]
    public string imageTitle { get; set; }

    [AllowHtml]
    public string imageExcerpt { get; set; }

    [AllowHtml]
    public string imageDescription { get; set; }

    [AllowHtml]
    public string imageURL { get; set; }

    [Required]
    public int productID { get; set; }
}
}

ImageController.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using JustAdminIt.Areas.JBI.Models;
using JustAdminIt.Areas.JBI.ViewModels;
using JustAdminIt.Areas.JBI.DAL;
using System.IO;

namespace JustAdminIt.Areas.JBI.Controllers
{ 
public class ImageController : Controller
{
    private ImageContext db = new ImageContext();
    private ProductContext pb = new ProductContext();

    //
    // GET: /JBI/Image/

    public ViewResult Index()
    {
        //attempt new things
        int id = 1;
        IEnumerable<Image> image = db.Image.TakeWhile(x => x.productID == id);
        IEnumerable<Product> product = pb.Product.TakeWhile(x => x.productID == id);

        ImageViewModel piViewModel = new ImageViewModel(image, product);
        return View(piViewModel);
        //return View(db.Image.ToList());
    }

    //
    // GET: /JBI/Image/Details/5

    public ViewResult Details(int id)
    {
        Image image = db.Image.Find(id);
        return View(image);
    }

    //
    // GET: /JBI/Image/Create

    public ActionResult Create()
    {
        return View();
    } 

    //
    // POST: /JBI/Image/Create
    //this simply populates the DB values, but does not handle the file upload

    [HttpPost]
    public ActionResult Create(Image image)
    {
        if (ModelState.IsValid)
        {
            db.Image.Add(image);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(image);
    }

    [HttpPost]
    public ActionResult UploadFile(HttpPostedFileBase Filedata)
    {
        // Verify that the user selected a file
        if (Filedata != null && Filedata.ContentLength > 0)
        {
            // extract only the fielname
            var fileName = Path.GetFileName(Filedata.FileName);
            // store the file inside ~/App_Data/uploads folder
            var path = Path.Combine(Server.MapPath("~/Content/images/product/"), fileName);
            Filedata.SaveAs(path);
        }
        // redirect back to the index action to show the form once again
        return RedirectToAction("Index");
    }

    //
    // GET: /JBI/Image/Edit/5

    public ActionResult Edit(int id)
    {
        Image image = db.Image.Find(id);
        return View(image);
    }

    //
    // POST: /JBI/Image/Edit/5

    [HttpPost]
    public ActionResult Edit(Image image)
    {
        if (ModelState.IsValid)
        {
            db.Entry(image).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(image);
    }

    //
    // GET: /JBI/Image/Delete/5

    public ActionResult Delete(int id)
    {
        Image image = db.Image.Find(id);
        return View(image);
    }

    //
    // POST: /JBI/Image/Delete/5

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {            
        Image image = db.Image.Find(id);
        db.Image.Remove(image);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}
}

ImageViewModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Web.Mvc;
using JustAdminIt.Areas.JBI.Models;

namespace JustAdminIt.Areas.JBI.ViewModels
{
public class ImageViewModel
{
    public IEnumerable<ImageViewModel> Image { get; set; }
    public IEnumerable<ImageViewModel> Product { get; set; }

    public ImageViewModel(IEnumerable<Image> image, IEnumerable<Product> product)
    {
        IEnumerable<Image> Image = image;
        IEnumerable<Product> Product = product;
    }
}
}

【问题讨论】:

  • 你也可以显示你的ImageViewModel吗?
  • 刚刚添加了ImageViewModel,抱歉我一开始忘记包含了。

标签: c# asp.net-mvc-3 viewmodel ienumerable


【解决方案1】:

在您的 Index 操作方法中,您创建一个新的 ImageViewModel(),然后将其传递给您的视图,但您的视图需要一个 IEnumerable&lt;ImageViewModel&gt;

您正在做的是创建一个 ImageViewModel,然后将两个集合传递给它。这就像使用一个盒子来容纳两组物品,而不是使用一堆盒子,每个盒子都容纳两种不同物品中的一种。

您需要重新设计您的应用,以便以正确的方式生成项目。我不确定为什么您在这里有两个不同的数据上下文,但这会使事情变得非常复杂。

【讨论】:

  • Mystere Man - 在双重背景下得到了解决,我知道我需要做些什么。我想我对此感到困惑,我认为 ViewModel 是一种从两个或多个模型中仅提取所需项目的方法 - 并且基本上将它们组合起来以在一个视图中使用,避免将不必要的数据推送到 UI。
  • @SouthPlatte - 是的,一个 ViewModel 可用于组合多个模型,但你的问题是这些是来自两个不同上下文的两个完全不同的集合,所以没有简单的方法来获得哪个图像搭配哪个产品。更好的解决方案是将图像绑定到产品,然后从您的产品模型 (product.Image) 中获取它,这样您只需传入一个集合,并通过导航到相关图像来获取相关图像。由于我不知道您在这里使用的是哪种数据技术,因此很难进一步评论。
  • @Mistere Man - 使用 SQL2K8、EF 4 和一些 LINQ。我可以将上下文减少到一个,我知道我在那里的错误 - 所以也许这会有所帮助 - 因为我认为我可以做一个 LINQ 查询来做一个连接并返回我需要的东西。我将继续工作并在找到结果后发布结果。感谢您提供到目前为止的信息。
  • @Mistere Man - 感谢您的输入,上下文是起点,从那里滚雪球般地重构 ViewModel 并在控制器中执行 LINQ 查询以正确执行连接。
【解决方案2】:

因此,在阅读了大量资源后,在 @Mystere Man 和 @Travis J 的帮助下,代码“sn-ps”脱离了上下文,我有了解决方案。

首先,我为每个模型创建了一个新的 DbContext。我知道,笨蛋。所以这些都包含在他们自己的上下文文件中,我的 web.config 更快乐。

在一个上下文中显示多个源的示例 DbContext:

public class MyDataContext : DbContext
{
    public DbSet<Bundle> Bundle { get; set; }
    public DbSet<Image> Image { get; set; }
    public DbSet<Product> Product { get; set; }
    public DbSet<Siteconfig> Siteconfig { get; set; }
}

更新后的文件(只发布有变化的文件,如未在此处列出,则与原问题相同):

ImageViewModel.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using System.Web.Mvc;
using JustAdminIt.Areas.JBI.Models;

namespace JustAdminIt.Areas.JBI.ViewModels
{
    public class ImageViewModel
    {
        public Image Image { get; set; }
        public Product Product { get; set; }
    }
}

ImageController.cs

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using JustAdminIt.Areas.JBI.Models;
using JustAdminIt.Areas.JBI.ViewModels;
using JustAdminIt.Areas.JBI.DAL;
using System.IO;

namespace JustAdminIt.Areas.JBI.Controllers
{ 
public class ImageController : Controller
{
    //private ImageContext db = new ImageContext();
    //private ProductContext pb = new ProductContext();

    private JustBundleItContext db = new JustBundleItContext();

    //
    // GET: /JBI/Image/

    public ViewResult Index()
    {
        var model = from a in db.Product
                    join b in db.Image
                    on a.productID equals b.productID
                    select new ImageViewModel
                    {
                        Product = a,
                        Image = b
                    };
        return View(model.ToList());

    }

    //
    // GET: /JBI/Image/Details/5

    public ViewResult Details(int id)
    {
        Image image = db.Image.Find(id);
        return View(image);
    }

    //
    // GET: /JBI/Image/Create

    public ActionResult Create()
    {
        return View();
    } 

    //
    // POST: /JBI/Image/Create
    //this simply populates the DB values, but does not handle the file upload

    [HttpPost]
    public ActionResult Create(Image image)
    {
        if (ModelState.IsValid)
        {
            db.Image.Add(image);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        return View(image);
    }

    [HttpPost]
    public ActionResult UploadFile(HttpPostedFileBase Filedata)
    {
        // Verify that the user selected a file
        if (Filedata != null && Filedata.ContentLength > 0)
        {
            // extract only the fielname
            var fileName = Path.GetFileName(Filedata.FileName);
            // store the file inside ~/App_Data/uploads folder
            var path = Path.Combine(Server.MapPath("~/Content/images/product/"), fileName);
            Filedata.SaveAs(path);
        }
        // redirect back to the index action to show the form once again
        return RedirectToAction("Index");
    }

    //
    // GET: /JBI/Image/Edit/5

    public ActionResult Edit(int id)
    {
        Image image = db.Image.Find(id);
        return View(image);
    }

    //
    // POST: /JBI/Image/Edit/5

    [HttpPost]
    public ActionResult Edit(Image image)
    {
        if (ModelState.IsValid)
        {
            db.Entry(image).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(image);
    }

    //
    // GET: /JBI/Image/Delete/5

    public ActionResult Delete(int id)
    {
        Image image = db.Image.Find(id);
        return View(image);
    }

    //
    // POST: /JBI/Image/Delete/5

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {            
        Image image = db.Image.Find(id);
        db.Image.Remove(image);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
}
}

索引.cshtml

@model List<JustAdminIt.Areas.JBI.ViewModels.ImageViewModel>

@{
ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
    <th>
        Image Title
    </th>
    <th>
        Image Excerpt
    </th>
    <th>
        Image Description
    </th>
    <th>
        Image 
    </th>
    <th>
       Product ID
    </th>
    <th></th>
</tr>

@foreach (var item in Model) {

<tr>
    <td>
        @Html.DisplayFor(modelItem => item.Image.imageTitle)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Image.imageExcerpt)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Image.imageDescription)
    </td>
    <td>
        <img src="@Url.Content("~/Content/images/product/")@Html.DisplayFor(modelItem => item.Image.imageURL)" alt="Product Image" width="150" />
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.Product.productName)
    </td>

    <td>
        @Html.ActionLink("Edit", "Edit", new { id = item.Image.imageID }) |
        @Html.ActionLink("Details", "Details", new { id = item.Image.imageID }) |
        @Html.ActionLink("Delete", "Delete", new { id = item.Image.imageID })
    </td>
</tr>
}

</table>

在控制器中使用 LINQ 查询让我能够进行所需的连接,以查找与图像相关联的产品,同时大大简化了 ViewModel。现在在视图中,我可以从 item.Image 组或 item.Products 组中进行选择,知道它们已正确连接。

【讨论】:

    【解决方案3】:

    问题可能是由于您使用了TakeWhile

    “只要指定条件为真,就从序列中返回元素,然后跳过其余元素。” -http://msdn.microsoft.com/en-us/library/system.linq.enumerable.takewhile.aspx

    你可能想在那里使用Where (https://stackoverflow.com/a/5031771/1026459)

    db.Image.Where(x => x.productID == id).ToList();
    pb.Product.Where(x => x.productID == id).ToList();
    

    这只是其中的一部分。您正在传递一个包含这两个 IEnumerable 集的类。但是,您的视图正在接受该类的 IEnumerable。您正确传递,但未正确接收。

    @model JustAdminIt.Areas.JBI.ViewModels.ImageViewModel
    

    但是这里还有另一个问题。因为您的产品和图像在传递时不相关,所以您不能同时在两者上使用 foreach

    @foreach (var item in Model) {
    

    可能是

    @foreach (var item in Model.Product) {
    

    但由于刚刚列出的原因,这并不意味着有那么多图像。为了建立这种关系,您需要稍微重构。如何实现这一点取决于您。基本上你需要在你的模型中创建一个虚拟关系,或者使用一个包含两者的类。

    【讨论】:

    • Travis - hmmm...using db.Image.While... 引发了一个错误,虽然未在 ImagesModel 中定义,也许应该分享一些关于我试图完成的内容的上下文:我有 xx 个产品,每个产品包含任意 xx 个图像。我在图像管理器中有一个所有图像的基本列表,它显示了所有图像,我只需要根据图像表中的 fk_productID 加入产品即可显示与图像关联的产品名称/ID。
    • 感谢您的意见,我想我明白了,并将发布解决方案。我只是对我的 DbContext 和 ViewModel 项目感到愚蠢。看到您指出产品和图像不相关时,我开始思考。再次感谢!
    • @SouthPlatte - 哎呀!那应该是.Where(),我进行了编辑以反映正确的调用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多