Vytvoření objektů pro přenos dat (DTO)

Stáhnout dokončený projekt

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.