Data Transfer Object简称DTO。 在web api经常会遇到将数据库的实体数据传输给客户端,此时,客户端接收的数据是直接与后端数据库一一对应的。但是我们并不是总是希望客户端收到和数据库
Data Transfer Object简称DTO。
在web api经常会遇到将数据库的实体数据传输给客户端,此时,客户端接收的数据是直接与后端数据库一一对应的。但是我们并不是总是希望客户端收到和数据库中一摸一样的结构数据,所以,DTO的概念就诞生了,只将部分结构或者更改结构,发送给客户端。
一般用途有如下:
- 隐藏数据库中特殊的字段数据,这些数据不希望客户端看到。
- 省略部分属性,这样客户端可以减少负载。
- 有些对象包含嵌入的对象,从而方便客户端使用。
- 如果引入了服务层,那么可以起到解耦服务层和数据库层。
如一本Book,在数据库中,可能含有很多字段,比如Id、Title、Name、以及所属分类、创建时间、发版时间等等,但是我客户端在显示所有书目列表是,有时候只想需要三个字典Id、Title、以及AuthorName,只有想显示具体某本书时,才会显示书的详细信息,即使详细信息,也不会包含所有数据库中的信息。此时我们就可以定义一个BookDto,用于列表书目显示。
public class BookDto
{
public int Id { get; set; }
public string Title { get; set; }
public string AuthorName { get; set; }
}
然后在web api接口的Get方法中,只返回Dto的结构。如下代码,从Books对象中只提取Id、Title、Author.Name三个属性数据,赋值给新的BookDto对象,并将BookDto对象传输给客户端。
[Route("api/[controller]")]
[ApiController]
public class HelloController : ControllerBase
{
[HttpGet]
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;
}
}
如果想查看某个书目的详细信息,可以在定义一个BookDetailDto,此时可能需要显示Id、Title、Year、Price、AuthorName、Genre。
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; }
}
定义一个webapi,这里使用了一种新的定义返回数据的方式:返回类型使用IHttpActionResult,通过注解方式定义返回的数据类型
[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);
}
通过这个简单的例子,就可以看出DTO可以创建多个,每个DTO可以对应一种应用场景,引入DTO可以根据不同场景传输不同的数据,而不是都传输数据库中所有字段,这是优点,但是缺点就是增加了复杂度,数据库到客户端中间引入了DTO这个数据结构。