Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este momento, nuestra API web expone las entidades de base de datos al cliente. El cliente recibe datos que se asignan directamente a las tablas de base de datos. Sin embargo, eso no siempre es una buena idea. A veces quiere cambiar la forma de los datos que envía al cliente. Por ejemplo, puede que desee:
- Quite las referencias circulares (consulte la sección anterior).
- Oculte propiedades concretas que no se supone que los clientes vean.
- Omitir algunas propiedades para reducir el tamaño de la carga útil.
- Aplanar grafos de objetos que contienen objetos anidados, para que sean más accesibles para los clientes.
- Evite vulnerabilidades de "publicación excesiva". (Consulte Validación de modelos para obtener una explicación sobre la publicación excesiva).
- Desacopla el nivel de servicio de la capa de base de datos.
Para ello, puede definir un objeto de transferencia de datos (DTO). Un DTO es un objeto que define cómo se enviarán los datos a través de la red. Veamos cómo funciona con la entidad Book. En la carpeta Models, agregue dos clases 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; }
}
}
La BookDetailDto clase incluye todas las propiedades del modelo Book, excepto que AuthorName es una cadena que contendrá el nombre del autor. La BookDto clase contiene un subconjunto de propiedades de BookDetailDto.
A continuación, reemplace los dos métodos GET de la clase BooksController por versiones que devuelvan DTOs. Usaremos la instrucción LINQ Select para convertir de entidades Book a DTO.
// 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);
}
Este es el código SQL generado por el nuevo GetBooks método. Puede ver que EF traduce LINQ Select en una instrucción SELECT de SQL.
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]
Por último, modifique el método PostBook para devolver un 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);
}
Nota:
En este tutorial, vamos a convertir a DTO manualmente en el código. Otra opción es usar una biblioteca como AutoMapper que controla la conversión automáticamente.