Veri Aktarımı Nesneleri (DTO) Oluşturma
Şu anda web API'miz veritabanı varlıklarını istemcinin 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:
- Döngüsel başvuruları kaldırma (önceki bölüme bakın).
- İstemcilerin görüntülememesi gereken belirli özellikleri gizleyin.
- Yük boyutunu azaltmak 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.
- "Aşırı posta" 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 gerçekleştirmek 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
kitap modelinin tüm özelliklerini içerir, ancak bu AuthorName
, yazar adını barındıracak bir dizedir. sınıfı, BookDto
öğesinden BookDetailDto
özelliklerin bir alt kümesini içerir.
Ardından, sınıfındaki iki GET yöntemini DTO'lar BooksController
döndüren sürümlerle değiştirin. Book varlıklarını 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, DTO döndürmek için yöntemini değiştirin PostBook
.
[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);
}
Not
Bu öğreticide, kodda el ile DTO'lara dönüştürüyoruz. Bir diğer seçenek de otomatik olarak dönüştürmeyi işleyen AutoMapper gibi bir kitaplık kullanmaktır.