【问题标题】:foreach loop is displaying the same image for all posts in mvc viewforeach 循环在 mvc 视图中为所有帖子显示相同的图像
【发布时间】:2015-04-26 12:48:33
【问题描述】:

我似乎无法在我的应用程序中显示每个帖子的单独图片。所有图像都与列表中的最后一个相同。我需要将每个post 中的Pictures 转换为base64。在控制器中,我试图抓取每个帖子对象并将 Picture 属性转换为 base64,然后将其添加到 ViewBag.ImageToShow,但是 ImageToShow 只能容纳一个项目,因此我的所有图像都设置为我的 posts 中的最后一个图像列表。如果我为ViewBag 创建一个list<string> 作为下面提到的答案之一,我不知道如何正确索引它们。

型号:

    public partial class Post
    {
        public Post()
        {
            this.Tags = new HashSet<Tag>();
        }

        public int Id { get; set; }
        public string BlogUserEmail { get; set; }
        public int CategoryId { get; set; }
        public string Title { get; set; }
        public string ShortDescription { get; set; }
        public string Description { get; set; }
        public string Meta { get; set; }
        public string UrlSlug { get; set; }
        public bool Published { get; set; }
        public System.DateTime PostedOn { get; set; }
        public Nullable<System.DateTime> Modified { get; set; }
        public byte[] Picture { get; set; }

        public virtual BlogUser BlogUser { get; set; }
        public virtual Category Category { get; set; }
        public virtual ICollection<Tag> Tags { get; set; }
    }
}

控制器:

    public ActionResult Index()
    {
        var posts = db.Posts.Where(p => p.BlogUserEmail == User.Identity.Name).Include(p => p.BlogUser).Include(p => p.Category);
        foreach (var item in posts) // this is wrong 
        {
            byte[] buffer = item.Picture;
            ViewBag.ImageToShow = Convert.ToBase64String(buffer);
        }
        return View(posts.ToList());
    }

查看:

@for (int i = 0; i < Model.Count(); i += 3)
{
    <div class="row">
        @foreach (var item in Model.Skip(i).Take(3))
        {
            <div class="col-md-4 portfolio-item">
                <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">
                    <img class="img-responsive" src="@Html.Raw("data:image/jpeg;base64," + ViewBag.ImageToShow)" alt="">
                </a>
                <h3>
                    <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">@Html.DisplayFor(modelItem => item.Title)</a>
                </h3>

                <p>@Html.DisplayFor(modelItem => item.ShortDescription)</p>
                @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
                @Html.ActionLink("Details", "Details", new { urlslug = item.UrlSlug }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.Id })
            </div>
        }
    </div>
}

请注意,在创建动态布局时,我将 viewbag.Imagetoshow 放在 foreach 循环中。对于帖子列表中的每个图像,显示图像。但是,我无法将 viewbag 存储为列表并返回与 foreach 视图相关的正确图像,同时必须将图像转换为 base64。

【问题讨论】:

    标签: c# asp.net asp.net-mvc linq entity-framework


    【解决方案1】:

    您的代码的问题在于控制器中 Index 函数的循环。您在循环内设置ViewBag.ImageToShow,此变量只能保存一个对象,因此通过在循环中设置它会不断重置。

    你有两个选择:

    使用 ViewBag

    在您的控制器中,创建一个分别包含int, string、ID 和值的字典。在您的循环中,您需要为找到的每个图像添加一个条目。

        public ActionResult Index()
        {
            Dictionary<int, string> postImages = new Dictionary<int, string>();
            var posts = db.Posts.Where(p => p.BlogUserEmail == User.Identity.Name).Include(p => p.BlogUser).Include(p => p.Category);
            foreach (var item in posts) 
            {
                byte[] buffer = item.Picture;
                postImages.Add(item.ID, Convert.ToBase64String(buffer));
            }
            ViewBag.Images = postImages;
            return View(posts.ToList());
        }
    

    在您的视图中,您应该在开始循环之前声明一个保存列表的局部变量:

    var images = (Dictionary<int, string>)ViewBag.Images;
    

    现在在循环中,您可以从列表中获取所需的图像:

    images.Single(i => i.Key == item.ID).Value;
    

    根据要求提供完整示例:

    @{Dictionary<int, string> images = (Dictionary<int, string>)ViewBag.Images;
    @for (int i = 0; i < Model.Count(); i += 3)
    {
        <div class="row">
            @foreach (var item in Model.Skip(i).Take(3))
            {
                <div class="col-md-4 portfolio-item">
                    <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">
                        <img class="img-responsive" src="@Html.Raw("data:image/jpeg;base64," + images.Single(image => image.Key == item.ID).Value)" alt="">
                    </a>
                    <h3>
                        <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">@Html.DisplayFor(modelItem => item.Title)</a>
                    </h3>
    
                    <p>@Html.DisplayFor(modelItem => item.ShortDescription)</p>
                    @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
                    @Html.ActionLink("Details", "Details", new { urlslug = item.UrlSlug }) |
                    @Html.ActionLink("Delete", "Delete", new { id = item.Id })
                </div>
            }
        </div>
      }
    }
    

    使用第二个模型

    我个人以前从未使用过它,但我已经看到它完成了,我想我只是提一下。

    要使用此技术,您必须创建第二个模型,此模型将包含您的原始模型和图像。

    public class PostImageModel
    {
        public Post Post { get; set; }
        public string Image { get; set; }
    }
    

    现在在控制器内部,我们必须为新创建的模型创建一个新列表,并用所需的数据填充它:

        public ActionResult Index()
        {
            List<PostImageModel> postsWithImages = new List<PostImageModel>();
            var posts = db.Posts.Where(p => p.BlogUserEmail == User.Identity.Name).Include(p => p.BlogUser).Include(p => p.Category);
            foreach (var item in posts) 
            {
                item.Picture = null;
                postsWithImages.Add(new PostImageModel()
                {
                    Post = item,
                    Image = Convert.ToBase64String(item.Picture)
                });
            }
            return View(postsWithImages);
        }
    

    编辑:添加以便代码在完成转换后清空字节数组。 (感谢@Serv)。

    您的循环将保持不变,除了您现在应该使用item.Post.Title 而不是使用item.Title

    请注意,您现在应该在 View 顶部添加 @model ExampleNamespace.Models.PostImageModel 以使用此模型。

    【讨论】:

    • ViewModel 是正确的方法,但现在您将图像存储两次!一次作为Post 中的字节数组,第二次作为Image 属性中的字符串。这会给您的流量带来很大压力。
    • 这是怎么回事:images.Single(i => i.Key == item.Id);?
    • 我假设不是我的旧 ViewBag.ImageToShow 去哪里,因为我已经使用了 i
    • 我确实将 i 更改为 n 并将其放置在适当的位置,但是您的方法没有显示图像,我可能将 var 图像放置在错误的位置。
    • 我已经更新了答案并添加了一个视图应该是什么样子的示例。
    【解决方案2】:

    您将每张图片分配给同一个 ViewBag 属性,您总是看到同一个是正常的。

    你应该添加一个新的字符串属性,用 NotMapped 属性标记,命名为 ImageToShow 到模型:

    public partial class Post
    {
        public Post()
        {
            this.Tags = new HashSet<Tag>();
        }
    
        public int Id { get; set; }
        public string BlogUserEmail { get; set; }
        public int CategoryId { get; set; }
        public string Title { get; set; }
        public string ShortDescription { get; set; }
        public string Description { get; set; }
        public string Meta { get; set; }
        public string UrlSlug { get; set; }
        public bool Published { get; set; }
        public System.DateTime PostedOn { get; set; }
        public Nullable<System.DateTime> Modified { get; set; }
        public byte[] Picture { get; set; }
    
        [NotMapped]
        public string ImageToShow { get; set; }
    
        public virtual BlogUser BlogUser { get; set; }
        public virtual Category Category { get; set; }
        public virtual ICollection<Tag> Tags { get; set; }
    }
    

    控制器:

        public ActionResult Index()
        {
            var posts = new List<MvcApplication2.Models.Post>();
            foreach (var item in posts) // this is wrong 
            {
                byte[] buffer = item.Picture;
                item.ImageToShow = Convert.ToBase64String(buffer);
            }
            return View(posts.ToList());
        }
    

    查看:

    @for (int i = 0; i < Model.Count(); i += 3)
    {
        <div class="row">
            @foreach (var item in Model.Skip(i).Take(3))
            {
                <div class="col-md-4 portfolio-item">
                    <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">
                        <img class="img-responsive" src="@Html.Raw("data:image/jpeg;base64," + item.ImageToShow)" alt="">
                    </a>
                <h3>
                    <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">@Html.DisplayFor(modelItem => item.Title)</a>
                </h3>
    
                <p>@Html.DisplayFor(modelItem => item.ShortDescription)</p>
                @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
                @Html.ActionLink("Details", "Details", new { urlslug = item.UrlSlug }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.Id })
            </div>
            }
        </div>
    }    
    

    【讨论】:

    • 你能扩展你的答案吗?
    【解决方案3】:

    有几种方法可以做到这一点。第一个也是最快的方法是将所有图像放在一个集合中,然后将该集合存储在 ViewBag 中。现在,您只在其中存储一张图像 - 最后一张。

    public ActionResult Index()
    {
        var posts = db.Posts.Where(p => p.BlogUserEmail == User.Identity.Name).Include(p => p.BlogUser).Include(p => p.Category);
    
        // create collection
        List<string> ImageCollection = new List<string>();
        foreach (var item in posts)
        {
            //add images to list
            ImmageCollection.Add(Convert.ToBase64String(item.Picture));
        }
        //Add list to ViewBag
        ViewBag.ImageToShow = ImageCollection;
        return View(posts.ToList());
    }
    

    现在您可以使用迭代器在视图中访问它:

    <img class="img-responsive" src="@Html.Raw("data:image/jpeg;base64," + ViewBag.ImageToShow[i])" alt="">
    

    就我个人而言,我会为此创建一个 ViewModel,因为您不会使用 Post 类的所有属性。

    创建一个包含您需要的属性的类。让我们称之为 PortfolioVM:

    public class PortfolioVM
    {
        public int Id { get; set; }
        public string UrlSlug { get; set; }
        //not Byte[]!
        public string Picture { get; set; }
        public string ShortDescription { get; set; }
    }
    

    现在在您的控制器中,我们将使用该 ViewModel:

    public ActionResult Index()
    {
        var posts = db.Posts.Where(p => p.BlogUserEmail == User.Identity.Name)
                            .Include(p => p.BlogUser)
                            .Include(p => p.Category)
                            .Select(vm => new PortfolioVM(){
                                Id = vm.Id,
                                UrlSlug = vm.UrlSlug,
                                Picture = Convert.ToBase64String(vm.Picture),
                                ShortDescription = vm.ShortDescprition
                             }) ;
    
        return View(posts);
    }
    

    现在我们需要更改您的视图,以使用新的 ViewModel。将声明模型的第一行更改为:

    //insert correct namespace!
    @model IEnumerable<Namespace.Subfolder.PortfolioVM)
    

    在此之后,您可以像显示视图的所有其他属性一样显示您的图像:

        @foreach (var item in Model.Skip(i).Take(3))
        {
            <div class="col-md-4 portfolio-item">
                <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">
                    <img class="img-responsive" src="@Html.Raw("data:image/jpeg;base64," + item.Picture)" alt="">
                </a>
                <h3>
                    <a href="@Url.Action("Details", "Post", new { urlslug = item.UrlSlug })">@Html.DisplayFor(modelItem => item.Title)</a>
                </h3>
    
                <p>@Html.DisplayFor(modelItem => item.ShortDescription)</p>
                @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
                @Html.ActionLink("Details", "Details", new { urlslug = item.UrlSlug }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.Id })
            </div>
        }
    

    【讨论】:

    • 现在它显示的是第一张图片而不是最后一张。
    • 不要使用 ViewBag。使用视图中的嵌套循环正确处理它真的很棘手。使用视图模型。如果你想将你的项目排成一行,有 3 个项目,我建议你使用像 Masonry 这样的 js 库。
    • 为什么不是一个字节?我的数据库表是 varbinary(max) 类型的,EF 模型会自动创建一个字节数组。
    • 因为您在视图中将其显示为字符串,不是吗?将其存储为 byte[] 没有意义
    【解决方案4】:

    你做错了!您将所有图像存储在同一个变量中!

    public ActionResult Index()
    {
        var posts = db.Posts.Where(p => p.BlogUserEmail == User.Identity.Name).Include(p => p.BlogUser).Include(p => p.Category);
    
            // this is what you should do, or something like this
            ViewBag.ImagesToShow = new List<String>();
    
        foreach (var item in posts)
        {
            byte[] buffer = item.Picture;
    
            // this is really wrong!
            //ViewBag.ImageToShow = Convert.ToBase64String(buffer);
    
            ViewBag.ImagesToShow.Add(Convert.ToBase64String(buffer));
        }
        return View(posts.ToList());
    }
    

    你还需要改变你的看法

    【讨论】:

    • 您也在每次迭代中分配属性!同样的错误。
    • 您在每次迭代中重新分配 ViewBag 属性。您应该将初始化移到循环之外。
    • 输出如下所示:src="data:image/jpeg;base64,System.Collections.Generic.List`1[System.String]"
    • @GarrithGraham 好吧,显然您需要从ImagesToShow 列表中获取正确的索引。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多