gpt4 book ai didi

c# - 这是如何使用 Entity Framework Core 和 ASP.NET Core MVC 2.2+ 和 3.0 创建数据传输对象 (DTO)

转载 作者:行者123 更新时间:2023-11-30 12:19:46 30 4
gpt4 key购买 nike

在使用 ASP.NET Core MVC 2.2 创建 RESTful Api 时,我注意到没有像 2014 web api 示例那样的 DTO 示例。

ASP.NET Core MVC 2.2 Rest api 2019 example

ASP.NET web-api 2014 example

因此,我决定为我的一些 Controller 动词 HTTPGet、HTTPPost 和 HTTPPut 创建 DTO

我的最终结果有 2 个问题。

  1. 这是一般意义上的推荐方法吗?或者,新的 Entity Framework Core 中是否有某些东西不同于或优于基于 Entity Framework 6 或更早版本的 2014 示例?

  2. 一般情况下应该使用 DTO 设计模式吗?或者 Entity Framework Core 中有什么东西完全不同于 DTO 模式。具体来说,有没有一种方法可以从数据库中获取数据并将其以我需要的确切方式传递给 View /客户端?

第 2 部分提问原因的更多背景信息。我读到过 DTO 是反模式,人们说出于某种原因不要使用它们。然而,许多开发人员恳求它们的使用以及何时以及为什么应该使用它们。我个人的一个例子是工作以及 Angular 和 React 项目。接收我需要的数据是一件美妙的事情,我无法想象任何其他替代方案,即进行所有类型的箍和解析以通过整体对象以在屏幕上显示地址和位置。

但是时代变了,有没有一种设计模式或另一种模式可以做完全相同的事情,但费用和计算成本更低。

  1. 就此而言,使用此模式对服务器和数据库服务器来说是否会产生很大的计算成本?

  2. 最后,下面的代码是人们期望如何在 Entity Framework Core 中使用 DTO 模式而不是 EF 6 或 linq to sql 框架?

我在下面包含了代码更改,以说明我在下面的练习中为 TodoItem 模型创建的 DTO。

项目(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; }
}
}

项目(TodoApi) --> Controller --> 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 Framework(Core 或 no)是抽象对象持久性的对象/关系映射器 (ORM)。提供给 EF 的“实体”是一个表示持久层中对象的类(即特定表中的一行)。就是这样。

但是,因此,它在其他情况下通常并不是非常有用。 SRP(单一职责原则)几乎规定实体应该只关注对持久性很重要的实际内容。处理特定请求、为特定 View 提供数据等的需求可能并且将会与此不同,这意味着您要么需要让实体类做太多事情,要么您需要专门用于这些目的的额外类。这就是 DTO、 View 模型等概念发挥作用的地方。

简而言之,正确的做法是在特定情况下使用有意义的东西。如果您正在处理 CRUD 类型的 API,那么在该场景中直接使用实体类可能是有意义的。然而,通常情况下,即使在 CRUD 的情况下,通常最好还是有一个自定义类来绑定(bind)请求主体。这使您可以控制诸如序列化以及哪些属性可查看、可编辑等内容。从某种意义上说,您正在将 API 与持久层分离,允许两者相互独立工作。

例如,假设您需要更改实体的属性名称。如果您的 API 直接使用该实体,那么这将需要对 API 进行版本控制并处理使用旧属性名称弃用先前版本的问题。为每个使用一个单独的类,您可以简单地更改映射层,API 会在不知不觉中愉快地运行。与 API 交互的客户端不需要更改。作为一般规则,您希望始终追求组件之间耦合度最低的路径。

关于c# - 这是如何使用 Entity Framework Core 和 ASP.NET Core MVC 2.2+ 和 3.0 创建数据传输对象 (DTO),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55999358/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com