简体   繁体   English

ASP.NET MVC 与视图模型的多对多关系

[英]ASP.NET MVC Many-to-many relationship with viewmodel

I'm trying to display a list of books that will show the details for each book.我正在尝试显示一个书籍列表,其中将显示每本书的详细信息。 It all works properly, except for the properties which have many-to-many relationships with the Books model.除了与 Books 模型具有多对多关系的属性外,一切正常。
Here is my Book model (I removed annotations for readability):这是我的 Book 模型(为了便于阅读,我删除了注释):

public class Book
{
    public int BookId { get; set; } 
    public string title { get; set; }
    public Int32 isbn { get; set; }
    public string author { get; set; }
    public string summary { get; set; }
    public string series { get; set; }
    public string amazonLink { get; set; }
    public string pubLink { get; set; }
    public int? GradeLevelId { get; set; } //Foreign Key for GradeLevel
    public bool needsEdit { get; set; }
    public int? LexileLevelId { get; set; } //Foreign Key for LexileLevel
    public DateTime dateAdded { get; set; }
    public Book()
    {
        dateAdded = DateTime.Now;
    }

    public string comments { get; set; }

    public virtual GradeLevel GradeLevel { get; set; }
    public virtual LexileLevel LexileLevel { get; set; }

    //Navigation Properties
    public virtual ICollection<Recommendation> Recommendations { get; set; }
    public virtual ICollection<RelevantGenre> RelevantGenres { get; set; }

}

The two navigation properties (Recommendation and RelevantGenre) are for the associative/joining tables, and that's where I'm having issues.两个导航属性(Recommendation 和 RelevantGenre)用于关联/联接表,这就是我遇到问题的地方。 To keep things simple, I'm going to focus on the RelevantGenre model.为简单起见,我将重点介绍 RelevantGenre 模型。 Each book can have more than one Genre, so the RelevantGenre is the join table between Book and Genre.每本书可以有多个流派,因此 RelevantGenre 是 Book 和 Genre 之间的连接表。
Here's the Model for those:这是那些的模型:

public class RelevantGenre
{
    //Both are primary keys
    [Key]
    [Column(Order = 1)]
    public int BookId { get; set; } //Foreign Key to Book

    [Key]
    [Column(Order = 2)]
    public int genreId { get; set; } //Foreign Key to Genre

    public virtual Book Book { get; set; } //Nav property
    public virtual Genre Genre { get; set; } //Nav property
}

public class Genre
{
    public int GenreId { get; set; }
    public string genreTitle { get; set; }
    public int genreOrder { get; set; }

    //Navigation Property to RelevantGenre
    public ICollection<RelevantGenre> RelevantGenres { get; set; }
}

Here's the Controller:这是控制器:

// GET: Books
    public ActionResult Index(string filter, string searchString)
    {
        var viewModel = new BookListViewModel();

        if (String.IsNullOrEmpty(searchString) && String.IsNullOrEmpty(filter))
        {
            var results = from b in db.Books select b;
            var resultsList = (results.ToList());
            viewModel.Books = resultsList;
            return View(viewModel);
        }
        else
        {
            var results = from b in db.Books select b;
            //Filtering the book list
            switch (filter)
            {
                case "HR":
                    results = from b in db.Books
                              join r in db.Recommendations
                              on new { b.BookId } equals
                                  new { r.BookId }
                              where (r.RecommendationTypeId == 1)
                              select b;
                    break;

                default:
                    results = from b in db.Books select b;
                    break;
            }
            if(!String.IsNullOrEmpty(searchString))
            {
                //Search query results
                var searchResults = from b in db.Books
                    .Where(model => model.title.Contains(searchString) || model.author.Contains(searchString) 
                    || model.series.Contains(searchString)) 
                    select b;
                if (searchResults != null )
                {
                    results = searchResults;
                }
                else
                {
                    ViewBag.SpanText = "Sorry, no results founds. Please try your search again.";
                }
            }
            var resultsList = (results.ToList());
            viewModel.Books = resultsList;
            return View(viewModel);
        }

    }

As you can see, it's returning a viewModel, because I thought that made the most sense for how to return a combination of model data.如您所见,它返回一个 viewModel,因为我认为这对于如何返回模型数据的组合最有意义。
Here's the viewmodel:这是视图模型:

public class BookListViewModel
{

    public List<Book> Books { get; set; }
    public int BookId { get; set; }
    public string title { get; set; }
    public string author { get; set; }
    public Int32 isbn { get; set; }
    public string series { get; set; }

    public int? GradeLevelId { get; set; }
    public string gradeLevelName { get; set; }

    public int? LexileLevelId { get; set; }
    public string lexileLevelName { get; set; }

    public Recommendation Recommendation { get; set; }
    public int RecommendationTypeId { get; set; }
    public string recName { get; set; }

    public RelevantGenre RelevantGenre { get; set; }
    public int genreId { get; set; }

}

And lastly, here's the view:最后,这是观点:

@model FavBooks.ViewModels.BookListViewModel
@{
ViewBag.Title = "All Books";
Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>Books</h2>
<p class="details">
    @Html.ActionLink("Browse All Books in List Format", "FullList", "Books")

</p>

@foreach (var item in Model.Books)
{
    <div class="row fullBorder">
        <div class="col-md-2 col-sm-2">
            <img src="~/Content/Images/harrypotterbook.png" class="bookThumb" alt="Book Image" />
        </div>
        <div class="col-md-3 col-xs-3">
            <h3>
                <a href="@Url.Action("Details", "Books", new { id = item.BookId })" class="darkLink bookTitle">
                    @Html.DisplayFor(modelItem => item.title)
                </a>
            </h3>
            <h4>By @Html.DisplayFor(modelItem => item.author)</h4>
            <p><strong>ISBN:</strong> @Html.DisplayFor(modelItem => item.isbn)</p>
        </div>
        <div class="col-md-3 col-sm-4 bookMargins">
            @{
                if (item.series != null)
                {
                    <p><strong>Series:</strong> @Html.DisplayFor(modelItem => item.series) </p>
                }
                else
                {
                    <p></p>
                }
            }

            <p><strong>Grade Level:</strong> @Html.DisplayFor(modelItem => item.GradeLevel.gradeLevelName )</p>
            <p><strong>Lexile Level:</strong> @Html.DisplayFor(modelItem => item.LexileLevel.lexileLevelName)</p>

        </div>
        <div class="col-md-4 col-sm-3">
            @Html.ActionLink("View Book Details", "Details", "Books", new { id = item.BookId }, new { @class="btn btn-default btnBookDetails" })
        </div>

    </div>   
}

You can see that my view displays a list of items from Model.Books using a foreach loop.您可以看到我的视图使用 foreach 循环显示了来自 Model.Books 的项目列表。 For each book, I'd like it to also display the RelevantGenres that are connected to the book, but it's not letting me.对于每本书,我还希望它显示与该书相关的 RelevantGenres,但它不允许我这样做。 The GradeLevel and LexileLevel properties connect just fine (those are one-to-many), but it doesn't seem to register any of the many-to-many relationships which are not directly part of the Book model. GradeLevel 和 LexileLevel 属性连接得很好(它们是一对多的),但它似乎没有注册任何不直接属于 Book 模型的多对多关系。
I feel like I'm missing something basic here, or maybe there's an issue with my view-model setup.我觉得我在这里缺少一些基本的东西,或者我的视图模型设置有问题。 Do you see where I went wrong on this or what I can do to display each book's genres?你看到我哪里出错了吗,或者我可以做些什么来显示每本书的类型?

EDIT:编辑:
Let me get more specific with what I tried.让我更具体地说明我的尝试。
I saw here that it's possible to use a foreach inside of another foreach to display a loop.我在这里看到可以在另一个 foreach 中使用 foreach 来显示循环。 But when I try that, it tells me that the "foreach cannot operate on that... because Favbooks.Models.Book does not contain a public definition for GetEnumerator".但是当我尝试这样做时,它告诉我“foreach 无法对其进行操作......因为 Favbooks.Models.Book 不包含 GetEnumerator 的公共定义”。 So I tried changing the @model to an IEnumerable<> and looping through the whole Model ( instead of foreach(var item in Model.Books) but then it still wouldn't work. In that situation, it gave me an error saying:因此,我尝试将 @model 更改为 IEnumerable<> 并遍历整个模型(而不是foreach(var item in Model.Books)但它仍然无法正常工作。在这种情况下,它给了我一个错误:

'BookListViewModel' does not contain a definition for 'RelevantGenres' and no extension method 'RelevantGenres' accepting a first argument of type 'BookListViewModel' could be found (are you missing a using directive or an assembly reference?) “BookListViewModel”不包含“RelevantGenres”的定义,并且找不到接受“BookListViewModel”类型的第一个参数的扩展方法“RelevantGenres”(您是否缺少 using 指令或程序集引用?)

Because that wasn't working, I kept the @model with @model FavBooks.ViewModels.BookListViewModel like it was initially, and and tried putting in @Html.DisplayFor(modelItem => item.Genres.genreTitle) but it doesn't recognize Genre or RelevantGenre.因为那不起作用,我将@model 与@model FavBooks.ViewModels.BookListViewModel保持原样,并尝试放入@Html.DisplayFor(modelItem => item.Genres.genreTitle)但它无法识别流派或相关流派。
To sum up, the issue is that if I loop through Model.Books, then it won't recognize anything in the viewmodel other than the Books list.总而言之,问题是如果我遍历 Model.Books,那么它不会识别除 Books 列表之外的视图模型中的任何内容。 But if I loop through the overall Model, then it still won't recognize the RelevantGenres, and now it started giving me another error like this:但是如果我遍历整个模型,那么它仍然无法识别 RelevantGenres,现在它开始给我另一个这样的错误:

The model item passed into the dictionary is of type 'FavBooks.ViewModels.BookListViewModel', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[FavBooks.ViewModels.BookListViewModel]'.传递到字典中的模型项的类型为“FavBooks.ViewModels.BookListViewModel”,但此字典需要类型为“System.Collections.Generic.IEnumerable`1[FavBooks.ViewModels.BookListViewModel]”的模型项。

I'm sorry if this isn't totally clear.如果这不完全清楚,我很抱歉。 I haven't worked so much with viewmodels before and I see that I must have set it up wrong, but I just don't know how to get this working...我以前没有用过这么多视图模型,我发现我一定是设置错了,但我只是不知道如何让它工作......

Have you tried .Include() ?你试过.Include()吗? This will populate all the related data in the navigation property.这将填充导航属性中的所有相关数据。 Refer https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework-in-an-asp-net-mvc-application for more detials.请参阅https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/reading-related-data-with-the-entity-framework -in-an-asp-net-mvc-application了解更多详情。

eg:例如:

var BookList = _context.Book.Where(m=>m.BookId==id).Include(m=>m.Recommendations).Include(m=>m.RelevantGenres)

This will query a record of Book whose BookId=id and will populate all the related navigation properties.这将查询 BookId=id 的 Book 记录,并将填充所有相关的导航属性。

There's also .ThenInclude()还有.ThenInclude()

A property of public List<Book> Books { get; set; } public List<Book> Books { get; set; } public List<Book> Books { get; set; } public List<Book> Books { get; set; } should be enough as this will store all the data related to your retrieved entity. public List<Book> Books { get; set; }应该足够,因为这会保存所有与您检索实体的数据。

and below is example of how you can access the fields of a navigation property.下面是如何访问导航属性字段的示例。

@model BookListViewModel

foreach(var b in Model.Books)
{
  @HtmlDisplayFor(modelItem=>b.title)

  foreach(var r in b.RelevantGenres)
  {
    @HtmlDisplayFor(modelItem=>r.GenereName)
  }
}

Myself being fairly new to .netcore mvc, I don't know much either.我自己对 .netcore mvc 还很陌生,我也不太了解。 But I hope this helps to point you in the right direction if not completely solve your issue.但我希望这有助于为您指明正确的方向,如果不能完全解决您的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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