ASP.NET MVC与viewmodel的多对多关系

时间:2017-05-14 21:24:23

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

我试图显示一系列书籍,这些书籍将显示每本书的详细信息。除了与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来显示循环。但是,当我尝试这个时,它告诉我&#34; foreach无法操作...因为Favbooks.Models.Book不包含GetEnumerator的公共定义&#34;。所以我尝试将@model更改为IEnumerable&lt;&gt;并循环遍历整个模型(而不是 foreach(var item in Model.Books),但它仍然无法正常工作。在这种情况下,它给了我一个错误说:

  

&#39; BookListViewModel&#39;不包含&#39; RelevantGenres&#39;的定义没有延伸方法&#39; RelevantGenres&#39;接受类型&#39; BookListViewModel&#39;的第一个参数。可以找到(你错过了使用指令或程序集引用吗?)

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

  

传递到字典中的模型项类型为&#39; FavBooks.ViewModels.BookListViewModel&#39;,但此字典需要类型为&#39; System.Collections.Generic.IEnumerable`1 [FavBooks]的模型项.ViewModels.BookListViewModel]&#39;

对不起,如果这还不完全清楚的话。我之前没有和viewmodels一起工作,我发现我一定是设置错了,但我不知道如何让它工作......

1 个答案:

答案 0 :(得分:0)

您尝试过.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<Book> 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还是很陌生,我也不是很了解。但是我希望这可以帮助您指出正确的方向,即使不能完全解决您的问题。