我正在嘗試建立一個圖書館。在我的主頁上,我設法顯示所有包含所有詳細資訊的書籍。當我點擊作者頁面時,我想顯示他的所有書籍(圖片、標題和作者)。問題是當我的書有多個作者時,我無法顯示所有作者。
我的主頁: 截圖
作者: 截圖
楷模:
public class Book : IEntityBase
{
[Key]
public int Id { get; set; }
public string Title { get; set; }
public DateTime RelaseDate { get; set; }
public string Description { get; set; }
public string ImageURL { get; set; }
public bool IsBorrowed { get; set; }
public string ISBN { get; set; }
//Relationships
public int PublisherId { get; set; }
[ForeignKey("PublisherId")]
public virtual Publisher Publisher { get; set; }
public int CategoryId { get; set; }
[ForeignKey("CategoryId")]
public virtual Category Categories { get; set; }
public virtual ICollection <Author_Book> Authors_Books { get; set; }
}
public class Author_Book
{
public int AuthorId { get; set; }
public virtual Author Author { get; set; }
public int BookId { get; set; }
public virtual Book Book { get; set; }
}
public class Author : IEntityBase
{
[Key]
public int Id { get; set; }
[Display(Name = "Autor")]
[Required(ErrorMessage = "Author jest wymagany")]
public string FullName { get; set; }
[Display(Name = "Biografia")]
[Required(ErrorMessage = "Biografia jest wymagana")]
public string Bio { get; set; }
[Display(Name = "Zdj?cie")]
public string ImageURL { get; set; }
//Relationships
public virtual ICollection<Author_Book> Authors_Books { get; set; }
public virtual ICollection<Author_Publisher> Author_Publisher { get; set; }
public virtual ICollection<Author_Category> Author_Category { get; set; }
}
作者控制器:
//GET: author/details
public async Task<IActionResult> Details(int id)
{
var authorDetails = await _service.GetAuthorByIdAsync(id);
if (authorDetails == null) return View("NotFound");
return View(authorDetails);
}
方法 GetAuthorbyIdAsync:
public async Task<Author> GetAuthorByIdAsync(int id)
{
var bookDetails = await _db.Authors
.Include(ab => ab.Authors_Books)
.ThenInclude(b => b.Book)
.FirstOrDefaultAsync(n => n.Id == id);
return bookDetails;
}
作者查看詳情
@model Author
<div class="row">
<div class="col-md-8 offset-2">
<div class="card mb-12">
<div class="row g-0">
<div class="col-md-12">
<div class="card-header">
<p class="card-text">
<h1 class="card-title">
@Model.FullName
</h1>
</p>
</div>
</div>
<div class="col-md-2">
<img src="@Model.ImageURL" width="100%" alt="@Model.FullName">
</div>
<div class="col-md-10">
<div class="card-body">
<p class="card-text">@Model.Bio</p>
</div>
</div>
<div class="col-md-12">
<h2 class="card-text text-center"> Wypo?ycz inne ksi??ki @Model.FullName:</h2>
<div class="row m-2">
@foreach (var book in Model.Authors_Books)
{
<div class="col-12 col-sm-6 col-xl-4">
<div class="card h-100" style="width: 250px;">
<div class="card-body text-center">
<img src="@book.Book.ImageURL" width="100%" alt="@book.Book.Title">
<br />
<br />
<h5 class="card-text"><b>@book.Book.Title</b></h5>
<h6 class="card-text"><b>@book.Author.FullName</b></h6>
<a class="btn btn-outline-primary" asp-controller="Book" asp-action="Details" asp-route-id="@book.Book.Id">
Wi?cej
</a>
</div>
</div>
</div>
}
</div>
</div>
<div class="col-md-12">
<div class="card-footer">
<p class="card-text">
<a class="btn btn-outline-success float-right" asp-action="Edit" asp-route-id="@Model.Id">Edytuj</a>
<a class="btn btn-outline-secondary" asp-controller="Book" asp-action="Index">Wró?</a>
</p>
</div>
</div>
</div>
</div>
</div>
uj5u.com熱心網友回復:
這里有兩個重要問題,第一個是您甚至沒有嘗試顯示Author與 相關的記錄Book,您正在顯示與書籍相關的記錄Book.Title而不是Author與書籍相關的記錄:
<h5 class="card-text"><b>@book.Book.Title</b></h5>
<h6 class="card-text"><b>@book.Author.FullName</b></h6>
至少你會期望
<h5 class="card-text"><b>@book.Book.Title</b></h5>
<h6 class="card-text"><b>@book.Book.Author.FullName</b></h6>
但是這些都行不通,因為有很多Author與本書相關的記錄,您在主頁上已正確演示。您可以使用這個簡單的 C# 運算式來創建與本書相關的作者的 CSV 字串:
String.Join(", ", book.Book.Authors_Books.Select(ba => ba.Author.FullName))
因此,您的模板將更改為:
<h5 class="card-text"><b>@book.Book.Title</b></h5>
<h6 class="card-text"><b>@(String.Join(", ", book.Book.Authors_Books.Select(ba => ba.Author.FullName)))</b></h6>
但要使其正常作業,我們需要通過附加一個附加項來告訴 EF 同時加載其他參考,該附加項ThenInclude將從資料庫中加載正確的資料。
public async Task<Author> GetAuthorByIdAsync(int id)
{
var authorAndBooks = await _db.Authors
.Include(author => author.Authors_Books)
.ThenInclude(authorBook => authorBook.Book)
.ThenInclude(book => book.Authors_Books)
.FirstOrDefaultAsync(author => author.Id == id);
return authorAndBooks;
}
*注意:我重命名了查詢中的一些變數以更好地表示它們相關的資料型別,看起來您誤解了 lambda 變數型別
如果您的視圖是在服務器端呈現的,或者您已強制序列化以包含參考,那么您可能看不到任何問題。
然而,當涉及到視圖和序列化時,結果并不總是你所期望的,當資料被發送回視圖時,在大多數情況下,它將使用故意排除遞回物體參考的邏輯進行序列化。發生這種情況時,結果圖的父級Author及其關聯Author_Book將不會包含在嵌套參考中。
看到這個小提琴:https ://dotnetfiddle.net/JCF4dB
您可以看到 的序列化輸出authorDetails在第二個陣列中只有一條記錄,Authors_Books這是意料之中的。為了解釋原因,請看一下 的第一個實體Authors_Books,注意該"Author"屬性被一起省略了,即使您在運行時通過代碼查詢它,它也會回傳最外層或父Author 級 Waris Dirie
{
"Id": 1,
"FullName": "Waris Dirie",
...
"Authors_Books": [
{
"Id": 1,
"AuthorId": 1,
"BookId": 1,
"Book": {}
}
]
}
如果它不排除參考,你最終會得到一個這樣的遞回回圈:
{
"Id": 1,
"FullName": "Waris Dirie",
...
"Authors_Books": [
{
"Id": 1,
"AuthorId": 1,
"Author":
{
"Id": 1,
"FullName": "Waris Dirie",
...
"Authors_Books": [
{
"Id": 1,
"AuthorId": 1,
"Author":
{
"Id": 1,
"FullName": "Waris Dirie",
...
"Authors_Books": [
{
"Id": 1,
"AuthorId": 1,
"Author": { ... }
"BookId": 1,
"Book": {}
}
]
},
"BookId": 1,
"Book": {}
}
]
},
"BookId": 1,
"Book": {}
}
]
}
相同的機制可防止一個陣列中的一個物件在一個完全不同的陣列中被參考,而該物件不會通過序列化有效負載進行投影。
One way to work around this is to recognise that if the Book is already linked to the Author via a parent Author_Book record, then we should use that reference first in the rendering list, appended with the other authors. To visualise this, change your template:
<h6 class="card-text">
<b>@book.Author.FullName, @(String.Join(", ", book.Book.Authors_Books.Select(ba => ba.Author.FullName)))</b>
</h6>
Or you can add the parent record into the child array, this handles cases where there is only a single author without any additional conditional logic. Use the Union function to create a single enumerable that contains all the records:
book.Book.Authors_Books.Union(new[] { book }).Select(ba => ba.Author.FullName)
Putting that back into the original template:
<h6 class="card-text">
<b>@book.Book.Authors_Books.Union(new[] { book }).Select(ba => ba.Author.FullName)))</b>
</h6>
The solution in these EF M:N (Many to Many) relationships is generally to avoid a recursive loop and only traverse the relationship in a single direction, but as you can see here, with a little bit of knowledge and understanding of how and why these data structure behave, we can work around the limitations imposed on us by the default serialization.
To further assist with identifying these data concepts as you are writing the code is to change your collection property names to be more meaningful, try either of these concepts:
public class Book : IEntityBase
{
[Key]
public int Id { get; set; }
...
public virtual ICollection <Author_Book> Book_Authors { get; set; }
}
public class Author : IEntityBase
{
[Key]
public int Id { get; set; }
...
public virtual ICollection <Author_Book> Author_Books { get; set; }
}
Or
public class Book : IEntityBase
{
[Key]
public int Id { get; set; }
...
public virtual ICollection <Author_Book> Authors { get; set; }
}
public class Author : IEntityBase
{
[Key]
public int Id { get; set; }
...
public virtual ICollection <Author_Book> Books { get; set; }
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/431515.html
標籤:C# asp.net-mvc 模型视图控制器 asp.net-mvc-5 引导程序 5
上一篇:從Json問題中提取詳細資訊
