Vytvoření objektů pro přenos dat (DTO)
V současné chvíli naše webové rozhraní API zpřístupňuje databázové entity klientovi. Klient obdrží data, která se mapují přímo na databázové tabulky. To ale není vždy dobrý nápad. Někdy chcete změnit tvar dat odesílaných do klienta. Můžete například chtít:
- Odebrání cyklických odkazů (viz předchozí část)
- Skryjte konkrétní vlastnosti, které klienti nemají zobrazit.
- Pokud chcete zmenšit velikost datové části, vynecháte některé vlastnosti.
- Zploštěte grafy objektů, které obsahují vnořené objekty, aby byly pro klienty pohodlnější.
- Vyhněte se chybám zabezpečení spočívajícím v nadměrném publikování. (Informace o nadměrném účtování najdete v tématu Ověření modelu .)
- Oddělte vrstvu služby od databázové vrstvy.
K tomu můžete definovat objekt přenosu dat (DTO). DTO je objekt, který definuje způsob odesílání dat přes síť. Podívejme se, jak to funguje s entitou Book. Do složky Models přidejte dvě třídy 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; }
}
}
Třída BookDetailDto
obsahuje všechny vlastnosti z modelu Book s výjimkou AuthorName
řetězce, který bude obsahovat jméno autora. Třída BookDto
obsahuje podmnožinu vlastností z BookDetailDto
.
Dále nahraďte dvě metody GET ve BooksController
třídě verzemi, které vracejí objekty DTO. K převodu entit Book na DTO použijeme příkaz LINQ Select .
// 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);
}
Tady je SQL vygenerovaný novou GetBooks
metodou. Uvidíte, že EF překládá LINQ Select na příkaz 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]
Nakonec upravte metodu PostBook
tak, aby vrátila 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);
}
Poznámka
V tomto kurzu převádíme na objekty DTO ručně v kódu. Další možností je použít knihovnu, jako je AutoMapper , která zpracovává převod automaticky.