【发布时间】:2019-09-23 17:52:14
【问题描述】:
在使用 ASP.NET Core MVC 2.2 创建 RESTful Api 时,我注意到没有像 2014 Web api 示例那样的 DTO 示例。
ASP.NET Core MVC 2.2 Rest api 2019 example
所以,我决定为我的一些控制器动词 HTTPGet、HTTPPost 和 HTTPPut 创建 DTO
我的最终结果中有 2 个问题。
这是一般意义上的推荐方法吗?或者,新的 Entity Framework Core 中是否存在与 2014 年基于 Entity Framework 6 或更早版本的示例不同或更好的内容?
一般来说应该使用 DTO 设计模式吗?或者 Entity Framework Core 中是否存在与 DTO 模式完全不同的东西。具体来说,有没有办法从数据库中获取数据并将其传递给视图/客户端,这正是我需要传递的方式?
关于提出问题第 2 部分原因的更多背景信息。我已经阅读了有关 DTO 是反模式的信息,人们说不要出于某种原因使用它们。然而,许多开发人员恳求他们的使用以及何时以及为什么应该使用它们。我个人的一个例子是工作以及 Angular 和 React 项目。接收我需要的数据是一件美妙的事情,我无法想象任何其他选择,即执行所有类型的箍和解析以通过一个整体对象在屏幕上显示地址和位置。
但是随着时代的变化,是否有一种设计模式或另一种模式可以做同样的事情,但费用和计算成本更低。
就此而言,服务器和 dbserver 使用这种模式是否会产生很大的计算成本?
最后,与 EF 6 或 linq to sql 框架相比,下面的代码是否期望在 Entity Framework Core 中使用 DTO 模式?
我已经包含了下面的代码更改,以说明我在下面的练习中为 TodoItem 模型创建的 DTO。
Project(TodoApi) --> DTO --> TodoItemDTO.cs:
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace TodoApi.Models
{
public class TodoItemDTO
{
[Required]
public string Names { get; set; }
[DefaultValue(false)]
public bool IsCompletes { get; set; }
}
public class TodoItemDetailDTO
{
public long Id { get; set; }
[Required]
public string Names { get; set; }
[DefaultValue(false)]
public bool IsCompletes { get; set; }
}
}
Project(TodoApi) --> 控制器--> TodoController.cs:
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using TodoApi.Models;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace TodoApi.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
[ApiController]
public class TodoController: ControllerBase
{
private readonly TodoContext _context;
public TodoController(TodoContext context)
{
_context = context;
if (_context.TodoItems.Count() == 0)
{
// Create a new TodoItem if collection is empty,
// which means you can't delte all TodoItems.
_context.TodoItems.Add(new TodoItem { Name = "Item1" });
_context.SaveChanges();
}
// Console.WriteLine(GetTodoItems());
}
// Get: api/Todo
[HttpGet]
public async Task<ActionResult<IQueryable<TodoItem>>> GetTodoItems()
{
var todoItems = await _context.TodoItems.Select(t =>
new TodoItemDetailDTO()
{
Id = t.Id,
Names = t.Name,
IsCompletes = t.IsComplete
}).ToListAsync();
return Ok(todoItems);
// previous return statement
//return await _context.TodoItems.ToListAsync();
}
// Get: api/Todo/5
[HttpGet("{id}")]
[ProducesResponseType(typeof(TodoItemDetailDTO), 201)]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.Select(t =>
new TodoItemDetailDTO()
{
Id = t.Id,
Names = t.Name,
IsCompletes = t.IsComplete
}).SingleOrDefaultAsync(t => t.Id == id);
if (todoItem == null)
{
return NotFound();
}
return Ok(todoItem);
//var todoItem = await _context.TodoItems.FindAsync(id);
//////if (todoItem == null)
//{
// return NotFound();
//}
//return todoItem;
}
// POST: api/Todo
/// <summary>
/// Creates a TodoItem.
/// </summary>
/// <remarks>
/// Sample request:
///
/// POST /Todo
/// {
/// "id": 1,
/// "name": "Item1",
/// "isComplete": true
/// }
///
/// </remarks>
/// <param name="item"></param>
/// <returns>A newly created TodoItem</returns>
/// <response code="201">Returns the newly created item</response>
/// <response code="400">If the item is null</response>
[HttpPost]
[ProducesResponseType(typeof(TodoItemDTO), 201)]
[ProducesResponseType(typeof(TodoItemDTO), 400)]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem item)
{
_context.TodoItems.Add(item);
await _context.SaveChangesAsync();
_context.Entry(item).Property(x => x.Name);
var dto = new TodoItemDTO()
{
Names = item.Name,
IsCompletes = item.IsComplete
};
// didn't use because CreatedAtAction Worked
// return CreatedAtRoute("DefaultApi", new { id = item.Id }, dto);
return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, dto);
// original item call for new todoitem post
//return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, item);
}
// PUT: api/Todo/5
[HttpPut("{id}")]
[ProducesResponseType(typeof(TodoItemDTO), 201)]
[ProducesResponseType(typeof(TodoItemDTO), 400)]
public async Task<IActionResult> PutTodoItem(long id, TodoItem item)
{
if (id != item.Id)
{
return BadRequest();
}
_context.Entry(item).State = EntityState.Modified;
await _context.SaveChangesAsync();
var dto = new TodoItemDTO()
{
Names = item.Name,
IsCompletes = item.IsComplete
};
return CreatedAtAction(nameof(GetTodoItem), new { id = item.Id }, dto);
}
// DELETE: api/Todo/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
_context.TodoItems.Remove(todoItem);
await _context.SaveChangesAsync();
return NoContent();
}
}
}
【问题讨论】:
-
Entity 在 c# 中创建映射到数据库的类。映射只需要完成一次,而不是在代码中。您通常可以使用菜单在 VS 中创建类/映射。 2014 链接底部的参考资料提供了进行映射的工具。由于映射只需要完成一次(或者当数据库表架构发生变化时),我建议使用 VS 菜单映射到数据库。
-
@jdweng 不完全关注我是 C# 新手。你能举个例子来说明你在说什么。页面底部的工具用于面向 EF6 的项目,这就是为什么我不愿深入研究的原因。另外,VS 使用菜单到底是什么意思?
-
我指的是自动映射器,它会自动从数据库中创建 c# 类。
-
@jdweng 是的,但那是为 EF6 而不是 EFCore 并且为每个创建类到底是什么?如果我需要更改所看到的内容和/或命名约定,那将有什么帮助
-
EFCore 是 Entity 的轻量级版本。映射是实体。您不能同时使用实体和 DTO。请参阅:driver733.com/2018/10/08/entity-and-dto.html
标签: c# asp.net asp.net-core entity-framework-core dto