Erstellen von Datentransferobjekten (DTOs)

Abgeschlossenes Projekt herunterladen

Im Moment macht unsere Web-API die Datenbankentitäten für den Client verfügbar. Der Client empfängt Daten, die Ihren Datenbanktabellen direkt zugeordnet sind. Das ist jedoch nicht immer eine gute Idee. Manchmal möchten Sie die Form der Daten ändern, die Sie an den Client senden. Auf diese Weise können Sie z. B. folgende Vorgänge durchführen:

  • Entfernen Sie Zirkelverweise (siehe vorheriger Abschnitt).
  • Blendet bestimmte Eigenschaften aus, die Clients nicht anzeigen sollen.
  • Auslassen einiger Eigenschaften, um die Nutzlast zu verringern
  • Vereinfachen Sie Objektgraphen, die geschachtelte Objekte enthalten, um sie für Clients bequemer zu machen.
  • Vermeiden Sie "Overposting"-Sicherheitsrisiken. (Eine Erläuterung der Überbuchung finden Sie unter Modellvalidierung .)
  • Entkoppeln Sie Ihre Dienstebene von Ihrer Datenbankebene.

Um dies zu erreichen, können Sie ein Datenübertragungsobjekt (Data Transfer Object , DTO) definieren. Ein DTO ist ein Objekt, das definiert, wie die Daten über das Netzwerk gesendet werden. Sehen wir uns an, wie dies mit der Book-Entität funktioniert. Fügen Sie im Ordner Models zwei DTO-Klassen hinzu:

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; }
    }
}

Die BookDetailDto -Klasse enthält alle Eigenschaften aus dem Book-Modell, mit der Ausnahme, dass AuthorName eine Zeichenfolge ist, die den Namen des Autors enthält. Die BookDto -Klasse enthält eine Teilmenge der Eigenschaften von BookDetailDto.

Ersetzen Sie als Nächstes die beiden GET-Methoden in der BooksController -Klasse durch Versionen, die DTOs zurückgeben. Wir verwenden die LINQ Select-Anweisung , um Book-Entitäten in DTOs zu konvertieren.

// 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);
}

Hier sehen Sie die SQL-Instanz, die von der neuen GetBooks Methode generiert wird. Sie können sehen, dass EF die LINQ Select-Anweisung in eine SQL SELECT-Anweisung übersetzt.

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]

Ändern Sie schließlich die PostBook -Methode, um eine DTO zurückzugeben.

[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);
}

Hinweis

In diesem Tutorial wird im Code manuell in DTOs konvertiert. Eine weitere Möglichkeit besteht darin, eine Bibliothek wie AutoMapper zu verwenden, die die Konvertierung automatisch verarbeitet.