Aracılığıyla paylaş


Veri Aktarım Nesneleri (DTO' lar) Oluşturma

Tamamlanan Projeyi İndir

Şu anda web API'miz veritabanı varlıklarını istemcide kullanıma sunar. İstemci, doğrudan veritabanı tablolarınıza eşleyen verileri alır. Ancak bu her zaman iyi bir fikir değildir. Bazen istemciye gönderdiğiniz verilerin şeklini değiştirmek istersiniz. Örneğin, şunları yapmak isteyebilirsiniz:

  • Dairesel referansları kaldırın (önceki bölüme bakın).
  • İstemcilerin görüntülememesi gereken belirli özellikleri gizleyin.
  • Yük boyutunu küçültmek için bazı özellikleri atla.
  • İstemciler için daha kullanışlı hale getirmek için iç içe nesneler içeren nesne grafiklerini düzleştirme.
  • "Fazla paylaşım" güvenlik açıklarından kaçının. (Fazla gönderme tartışması için bkz. Model Doğrulama .)
  • Hizmet katmanınızı veritabanı katmanınızdan ayırma.

Bunu yapmak için bir veri aktarım nesnesi (DTO) tanımlayabilirsiniz. DTO, verilerin ağ üzerinden nasıl gönderileceğini tanımlayan bir nesnedir. Bunun Book varlığıyla nasıl çalıştığını görelim. Modeller klasörüne iki DTO sınıfı ekleyin:

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

sınıfı, BookDetailDto yazar adını tutacak bir dize dışında AuthorName Kitap modelindeki tüm özellikleri içerir. sınıfı, BookDto öğesinden BookDetailDtoözelliklerin bir alt kümesini içerir.

Ardından, BooksController sınıfında bulunan iki GET yöntemini DTO döndüren sürümlerle değiştirin. Book varlıklarından DTO'lara dönüştürmek için LINQ Select deyimini kullanacağız.

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

Yeni yöntem tarafından oluşturulan SQL aşağıdadır GetBooks . EF'nin LINQ Select'i bir SQL SELECT deyimine çevirdiğini görebilirsiniz.

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]

Son olarak, PostBook yöntemini bir DTO döndürecek şekilde değiştirin.

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

Uyarı

Bu öğreticide, kodda DTO'lara el ile dönüştürme yapıyoruz. Bir diğer seçenek de otomatik olarak dönüştürmeyi işleyen AutoMapper gibi bir kitaplık kullanmaktır.