【问题标题】:ASP.NET MVC Many-to-many relationship with viewmodelASP.NET MVC 与视图模型的多对多关系
【发布时间】:2017-10-13 14:46:21
【问题描述】:

我正在尝试显示一个书籍列表,其中将显示每本书的详细信息。除了与 Books 模型具有多对多关系的属性外,它都可以正常工作。
这是我的 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; }

}

两个导航属性(Recommendation 和 RelevantGenre)用于关联/连接表,这就是我遇到问题的地方。为简单起见,我将专注于 RelevantGenre 模型。每本书可以有多个类型,因此 RelevantGenre 是 Book 和 Genre 之间的连接表。
这是适合这些人的模型:

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; }
}

这是控制器:

// 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);
        }

    }

如您所见,它返回了一个 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; }

}

最后,这是视图:

@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>   
}

您可以看到,我的视图使用 foreach 循环显示了 Model.Books 中的项目列表。对于每本书,我希望它还显示与该书相关的 RelevantGenres,但它不允许我这样做。 GradeLevel 和 LexileLevel 属性连接得很好(它们是一对多的),但它似乎没有注册任何不直接属于 Book 模型的多对多关系。
我觉得我在这里缺少一些基本的东西,或者我的视图模型设置有问题。您是否看到我在这方面出了什么问题,或者我可以做些什么来显示每本书的流派?

编辑:
让我更具体地说明我的尝试。
我看到here 可以在另一个 foreach 中使用一个 foreach 来显示一个循环。但是当我尝试这样做时,它告诉我“foreach 无法对其进行操作......因为 Favbooks.Models.Book 不包含 GetEnumerator 的公共定义”。所以我尝试将@model 更改为 IEnumerable 并循环遍历整个模型( 而不是 foreach(var item in Model.Books) 但它仍然无法正常工作。在那种情况下,它给了我一个错误说:

“BookListViewModel”不包含“RelevantGenres”的定义,并且找不到接受“BookListViewModel”类型的第一个参数的扩展方法“RelevantGenres”(您是否缺少 using 指令或程序集引用?)

因为这不起作用,我将@model 保留为@model FavBooks.ViewModels.BookListViewModel,就像最初一样,并尝试输入@Html.DisplayFor(modelItem =&gt; item.Genres.genreTitle),但它无法识别流派或相关流派。
总而言之, 问题在于,如果我遍历 Model.Books,则它不会识别视图模型中除 Books 列表之外的任何内容。但是如果我遍历整个模型,那么它仍然无法识别 RelevantGenres,现在它开始给我另一个类似这样的错误:

传入字典的模型项的类型为“FavBooks.ViewModels.BookListViewModel”,但此字典需要类型为“System.Collections.Generic.IEnumerable`1[FavBooks.ViewModels.BookListViewModel]”的模型项。

如果这不是完全清楚,我很抱歉。我以前没有用过太多的视图模型,我发现我一定是设置错了,但我只是不知道如何让它工作......

【问题讨论】:

  • 有什么问题?
  • 好吧,首先,您实际上并没有在视图中显示流派。你的意思是 RelevantGenre 是空的吗?那是因为你实际上并没有在任何地方设置它。如果您的意思是如果您浏览书籍的导航,那么很难知道,因为您没有显示任何实际尝试过的代码。仅供参考,书籍/类型没有多对多,它是一对多。
  • 我刚刚在问题中添加了更多细节,希望对您有所帮助。 @ErikFunkenbusch 你能解释一下我实际上没有设置 RelevantGenre 的意思吗?而且,我的设置有点不寻常,因为每本书都可以有多个流派,并且每个流派都应该能够提取具有该流派的书籍列表。那么在这种情况下,1 比多如何呢?
  • @Sh306 - 出于某种原因,您正在创建一个名为 ReleventGenre 的联结类,它实际上应该称为 BookGenre 或 GenreBook,以便让框架自动将其识别为联结表。事实上,您甚至不需要这个类,因为 EF 会自动使用名为 BookGenre 或 GenreBook 的表。您可以使用以下地图覆盖它:entityframeworktutorial.net/code-first/…,然后您将删除 ReleventGenres 类并让 Book 和 Genre 包含两者的集合。
  • 如果您打算拥有一个带有有效负载的联结表(除了联接之外的其他数据),您只需要 ReleventGenre 表

标签: c# asp.net-mvc entity-framework viewmodel navigation-properties


【解决方案1】:

你试过.Include()吗?这将填充导航属性中的所有相关数据。更多详情请参考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

例如:

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

这将查询 BookId=id 的 Book 记录,并将填充所有相关的导航属性。

还有.ThenInclude()

public List&lt;Book&gt; Books { get; set; } 的属性就足够了,因为它将存储与您检索到的实体相关的所有数据。

以下是如何访问导航属性字段的示例。

@model BookListViewModel

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

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

我自己对 .netcore mvc 还很陌生,我也不太了解。但如果不能完全解决您的问题,我希望这有助于为您指明正确的方向。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-20
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多