简体   繁体   English

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

[英]Is This How to Create a Data Transfer Object (DTO) with Entity Framework Core & ASP.NET Core MVC 2.2+ and 3.0

In creating a RESTful Api with ASP.NET Core MVC 2.2 I noticed there wasn't aa DTO example like the 2014 web api example. 在使用ASP.NET Core MVC 2.2创建RESTful Api时,我注意到没有像2014 web api示例那样的DTO示例。

ASP.NET Core MVC 2.2 Rest api 2019 example ASP.NET Core MVC 2.2 Rest api 2019示例

ASP.NET web-api 2014 example ASP.NET web-api 2014示例

So, I decided to create DTO's for a few of my controller verbs HTTPGet, HTTPPost and HTTPPut 所以,我决定为我的一些控制器动词HTTPGet,HTTPPost和HTTPPut创建DTO

I have 2 fold questions from my final result. 我的最终结果有2个问题。

  1. Is this the recommended way of doing it in a general sense. 这是一般意义上的推荐方式。 Or is there something in the new Entity Framework Core that is different or better than the 2014 example that was based on Entity Framework 6 or previous? 或者新实体框架核心中是否存在与基于实体框架6或之前版本的2014示例不同或更好的内容?

  2. Should one utilize the DTO design pattern in general? 一般应该使用DTO设计模式吗? Or is there something in the Entity Framework Core that is different from a DTO pattern altogether. 或者实体框架核心中是否存在与DTO模式完全不同的东西。 Specifically is there a way to take data from a database and pass it to the view/client the exact way I need it to be passed over? 具体来说有没有办法从数据库中获取数据并将其传递给视图/客户端我需要传递的确切方式?

More background to the reason for asking question part 2. I have read about DTO's being anti-patterns and people say don't use them for one reason or another. 提问问题第2部分的原因的更多背景。我读过DTO的反模式,人们说不要因为某种原因使用它们。 However, many developers implore their usage and when and why they should be used. 但是,许多开发人员都在考虑使用它们以及何时以及为何使用它们。 A personal example for me is working and Angular and React projects. 我的一个个人例子是工作和Angular和React项目。 Receiving data I need is a beautiful thing that I can't imagine any other the other alternative which would be to do all types of hoops and parsing to get through a monolithic object to display address and location onto the screen. 接收我需要的数据是一件美丽的事情,我无法想象其他任何其他替代方案,即做所有类型的箍和解析以通过单片对象来在屏幕上显示地址和位置。

But have times changed and is there a design pattern or another pattern that would do the exact same thing but at a lower expense and compute cost. 但是时间有所改变,是否有一种设计模式或其他模式可以做同样的事情,但成本和计算成本更低。

  1. For that matter is there a great compute cost to the server and dbserver for using this pattern? 对于这个问题,服务器和dbserver使用这种模式会有很大的计算成本吗?

  2. Lastly, is the code below how one would expect to utilize a DTO pattern in Entity Framework Core as opposed to the EF 6 or linq to sql frameworks? 最后,下面的代码是如何预期在Entity Framework Core中使用DTO模式而不是EF 6或linq到sql框架?

I have included the code changes below to illustrate my DTO creation for the TodoItem model from the below exercise. 我在下面的练习中包含了下面的代码更改,以说明我为TodoItem模型创建的DTO。

Project(TodoApi) --> DTOs --> TodoItemDTO.cs: 项目(TodoApi) - > DTOs - > 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) --> Controllers--> TodoController.cs: 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();
        }
    }
}

I think you're getting too hung up on semantics. 我觉得你太沉溺于语义了。 Strictly speaking, an "entity" is merely an object with identity (ie has an identifier), in contrast to something like a "value object". 严格地说,“实体”仅仅是具有身份的对象(即具有标识符),而不像“价值对象”。 Entity Framework (Core or no) is an object/relational mapper (ORM) that abstracts object persistence. 实体框架(核心或否)是一个对象/关系映射器(ORM),它抽象对象持久性。 The "entity" being fed to EF is a class that represents an object in the persistence layer (ie a row in a particular table). 馈送到EF的“实体”是表示持久层中的对象(即特定表中的行)的类。 That is all. 就这些。

However, as such, it's often not incredibly useful in other scenarios. 但是,在其他情况下,它通常不是非常有用。 The SRP (single-responsibility principle) pretty much dictates that the entity should concern itself only with the actual stuff that's important to persistence. SRP(单一责任原则)几乎要求实体只关注对持久性很重要的实际内容。 The needs of a handling a particular request, feeding a particular view with data, etc. can and will diverge from that, meaning you either need to make the entity class do too much or you need additional classes specifically for those purposes. 处理特定请求,向特定视图提供数据等的需求可能并且将与此不同,这意味着您需要使实体类做得太多,或者您需要专门用于这些目的的其他类。 That's where the concept of things like DTOs, view models, etc. come into play. 这就是DTO,视图模型等事物的概念发挥作用的地方。

In short, the correct thing to do is to use what makes sense in a particular circumstance. 简而言之,正确的做法是使用在特定环境中有意义的东西。 If you're dealing with a CRUD-type API, it might make sense to use the entity class directly in that scenario. 如果您正在处理CRUD类型的API,那么在该场景中直接使用实体类可能是有意义的。 However, more often than not, even in the scenario of CRUD, it's still usually preferable to have a custom class to bind the request body to. 但是,通常情况下,即使在CRUD的场景中,通常仍然希望有一个自定义类来绑定请求体。 That allows you to control things like serialization and which properties are viewable, editable, etc. In a sense, you're decoupling the API from the persistence layer, allowing the two to work independently of each other. 这允许您控制序列化以及哪些属性可查看,可编辑等等。在某种意义上,您将API与持久层分离,允许两者彼此独立工作。

For example, let's say you need to change the name of a property on your entity. 例如,假设您需要更改实体上的属性名称。 If your API uses the entity directly, then that would require versioning the API and dealing with deprecation of the previous version with the old property name. 如果您的API直接使用该实体,则需要对API进行版本控制并使用旧属性名称处理旧版本的弃用。 Using a separate class for each, you can simply change the mapping layer and the API goes along happily unaware. 为每个类使用一个单独的类,您只需更改映射层,并且API愉快地不知道。 No change is required by clients interacting with the API. 客户端与API交互不需要进行任何更改。 As a general rule, you want to always pursue the path of least coupling between components. 作为一般规则,您希望始终追求组件之间的最小耦合路径。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 asp.net core 2.2+ web 应用程序上的多个表单 - Multiple forms on an asp.net core 2.2+ web application ASP.NET Core MVC + Entity Framework Core 如何获取新添加的 object 的 id - How ASP.NET Core MVC + Entity Framework Core get the id of a newly added object ASP.NET Core 2.2 实体框架日志记录 - ASP.NET Core 2.2 Entity Framework Logging Entity Framework Core 和 ASP.NET Core MVC 中的 Inheritance 数据库 - Inheritance Database in Entity Framework Core and ASP.NET Core MVC ASP.NET Core 3.0 实体框架中的存储过程 - Stored Procs in ASP.NET Core 3.0 Entity Framework ASP.NET Core 2.2实体框架:在不更改上下文的情况下获取数据子集 - ASP.NET Core 2.2 Entity Framework: Get subset of data without altering context 检索和填充数据 ASP.Net Core 2.2 MVC - Retrieve and populate data ASP.Net Core 2.2 MVC 在 Entity Framework Core 和 ASP.NET Core 中加载相关数据 - Loading related data in Entity Framework Core and ASP.NET Core 如何在 ubuntu 桌面上使用 Entity Framework Core 将 asp.net core MVC 项目连接到本地数据库 - how to connect asp.net core MVC project to local DB using Entity Framework Core on ubuntu desktop 如何在 Entity Framework Core 和 ASP.NET Core MVC 中使用外键 - How work with foreign key in Entity Framework Core & ASP.NET Core MVC
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM