简体   繁体   中英

How to use FirstOrDefault inside Include

I have two entities for items and pictures. Each item can have multiple pictures, but I only want to return one picture of each item when I list the items.

Item class

public class Item 
{
    [Key]
    public int ItemID { get; set; }
    public string Name{ get; set; }
    public string Status{ get; set; }
    public virtual ICollection<Picture> Pictures{ get; set; } // Navigation property
}

Picture class

public class Picture
{
    [Key]
    public int PictureID { get; set; }
    public string Filename{ get; set; }
    public string Filepath{ get; set; }
    public int ItemID { get; set; } // Foreign Key
    public virtual Item Item{ get; set; } // Navigation property
}

Controller

public ActionResult Lager()
{
    var model = _db.Items.Include(b => b.Pictures.FirstOrDefault()).Where(i =>
    i.Status ==  0)

    return View(model);
}

I get this error from my controller

The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.

The view is strongly typed

@model IEnumerable<MyProject.Models.Koeretoej>

<div">
@foreach(var item in Model){
    <ul>
        <li>
            <span class="icon">
                <img src="@item.Pictures.Filepath@item.Billeder.Filepath"/>
            </span>
        </li>
        <li><span class="text">@item.Name</span></li>
        <li><span class="text">@item.Status</span></li>
    </ul>
}
</div>

And in my view I want to combine Filepath and Filename to use it for the img 's src. But I'm not sure how to do it.

I assume your are using Linq for Entities. Include specifies the related objects to include in the query results. This is useful when selecting something that is not directly on your Item entity, as Linq for Entities uses lazy loading. What i assume you are after, is only Selecting the first or default Picture on each of your Item. That would look something like this:

_db.Items.Where(i => i.Status == 0)
         .Select(i => new { Picture = i.Pictures.FirstOrDefault(), Item = i} )
         .ToList();

Update As mentioned by Arnold, the query is actually eager loading the first item. There is no need for .Include.

Update according to updated question Generally, it's better design to not pass all kinds of unnecessary thing to the view. Also, the query above creates an anonymous object in the select, that can't be passed to a view. Instead, make a specific model for your view. Perhaps like this:

ItemWithPicture.cs

public class ItemWithPicture 
{
    public int ItemID { get; set; }
    public string Name{ get; set; }
    public string Status{ get; set; }
    public string PictureFilename{ get; set; }
    public string PictureFilepath{ get; set; }
}

Controller

public ActionResult Lager()
{
    var model = _db.Items.Where(i => i.Status == 0).Select(i => new { Picture = i.Pictures.FirstOrDefault(), Item = i} ).Select(i => new {
              ItemID = i.Item.ItemID,
              Name =  i.Item.Name,
              Status = i.Item.Status,
              PictureFilename = i.Picture != null ? i.Picture.Filename : null,
              PictureFilepath = i.Picture != null ? i.Picture.Filepath : null
         }).ToList();
    return View(model);
}

View

@model IEnumerable<SomeNamespace.ItemWithPicture>

<div>
@foreach(var item in Model){
    <ul>
        <li>
            <span class="icon">
                <img src="@item.Filepath"/>
            </span>
        </li>
        <li><span class="text">@item.Name</span></li>
        <li><span class="text">@item.Status</span></li>
    </ul>
}
</div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM