建立資料傳輸物件 (DTO)
現在,我們的 Web API 會將資料庫實體公開給用戶端。 用戶端會接收直接對應至資料庫資料表的資料。 不過,這不一定是個不錯的主意。 有時候您想要變更傳送至用戶端的資料圖形。 例如,您可能要:
- (移除迴圈參考,請參閱上一節) 。
- 隱藏用戶端不應該檢視的特定屬性。
- 省略某些屬性,以減少承載大小。
- 包含巢狀物件的扁平化物件圖形,使其更方便用戶端使用。
- 避免「過度張貼」弱點。 (請參閱 模型驗證 ,以取得過度張貼的討論。)
- 將服務層與資料庫層分離。
若要達成此目的,您可以在 DTO) (定義 資料傳輸物件 。 DTO 是定義如何透過網路傳送資料的物件。 讓我們看看該如何與 Book 實體搭配運作。 在 Models 資料夾中,新增兩個 DTO 類別:
namespace BookService.Models
{
public class BookDto
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
}
}
namespace BookService.Models
{
public class BookDetailDto
{
public int Id { get; set; }
public string Title { get; set; }
public int Year { get; set; }
public decimal Price { get; set; }
public string AuthorName { get; set; }
public string Genre { get; set; }
}
}
類別 BookDetailDto
包含 Book 模型的所有屬性,不同之處在于 AuthorName
是保留作者名稱的字串。 類別 BookDto
包含 來自 BookDetailDto
的屬性子集。
接下來,將 類別中的 BooksController
兩個 GET 方法取代為傳回 DTO 的版本。 我們將使用 LINQ Select 語句,從 Book 實體轉換成 DTO。
// GET api/Books
public IQueryable<BookDto> GetBooks()
{
var books = from b in db.Books
select new BookDto()
{
Id = b.Id,
Title = b.Title,
AuthorName = b.Author.Name
};
return books;
}
// GET api/Books/5
[ResponseType(typeof(BookDetailDto))]
public async Task<IHttpActionResult> GetBook(int id)
{
var book = await db.Books.Include(b => b.Author).Select(b =>
new BookDetailDto()
{
Id = b.Id,
Title = b.Title,
Year = b.Year,
Price = b.Price,
AuthorName = b.Author.Name,
Genre = b.Genre
}).SingleOrDefaultAsync(b => b.Id == id);
if (book == null)
{
return NotFound();
}
return Ok(book);
}
以下是新 GetBooks
方法所產生的 SQL。 您可以看到 EF 會將 LINQ Select 轉譯成 SQL SELECT 語句。
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
[Extent2].[Name] AS [Name]
FROM [dbo].[Books] AS [Extent1]
INNER JOIN [dbo].[Authors] AS [Extent2] ON [Extent1].[AuthorId] = [Extent2].[Id]
最後,修改 PostBook
方法以傳回 DTO。
[ResponseType(typeof(BookDto))]
public async Task<IHttpActionResult> PostBook(Book book)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Books.Add(book);
await db.SaveChangesAsync();
// New code:
// Load author name
db.Entry(book).Reference(x => x.Author).Load();
var dto = new BookDto()
{
Id = book.Id,
Title = book.Title,
AuthorName = book.Author.Name
};
return CreatedAtRoute("DefaultApi", new { id = book.Id }, dto);
}
注意
在本教學課程中,我們會在程式碼中手動轉換成 DTO。 另一個選項是使用自動處理轉換的程式庫,例如 AutoMapper 。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應