【问题标题】:How to manually mapping DTO WITHOUT using AutoMapper?如何在不使用 AutoMapper 的情况下手动映射 DTO?
【发布时间】:2018-08-24 09:01:09
【问题描述】:

我正在学习 C#.NET Core 并尝试在不使用 AutoMapper 的情况下创建 DTO 映射,因为我正在单独处理一个小项目并且想在使用额外包之前了解基础知识,令人惊讶的是我无法在 stackoverflow 上轻松找到答案。 com,否则我可能会使用错误的关键字搜索。

顺便说一句,下面是我在 GetEmployee 方法下成功映射到 EmployeeForShortDto 的代码。不幸的是,我不知道如何将它映射到 GetAllEmployee 下,因为返回数据是一个集合,而不是单个记录。请指教。

EmployeeController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using NetCoreWebApplication1.Dto;
using NetCoreWebApplication1.Repository;
using NetCoreWebApplication1.Other;

namespace NetCoreWebApplication1.Controller
{
    [Route("api/[controller]")]
    [ApiController]
    public class EmployeeController : ControllerBase
    {
        private readonly IMasterRepository _repo;

        public EmployeeController(IMasterRepository repo)
        {
            _repo = repo;
        }

        [HttpGet("{id}")]
        public async Task<IActionResult> GetEmployee(int id)
        {
            var data = await _repo.GetEmployee(id);
            if (data == null) return NotFound();
            var dataDto = new EmployeeForShortDto()
            {
                Id = data.Id,
                EmpCode = data.EmpCode,
                Fname = data.Fname,
                Lname = data.Lname,
                Age = NetCoreWebApplication1.Other.Extension.CalcAge(data.DateBirth)
            };

            return Ok(dataDto);
        }

        [HttpGet]
        public async Task<IActionResult> GetAllEmployee()
        {
            var data = await _repo.GetAllEmployee();
            return Ok(data);
        }

    }
}

MasterRepository.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using NetCoreWebApplication1.Models;

namespace NetCoreWebApplication1.Repository
{
    public class MasterRepository : IMasterRepository
    {
        private readonly PrDbContext _context;

        public MasterRepository(PrDbContext context)
        {
            _context = context;
        }


        // Employee
        public async Task<List<Employee>> GetAllEmployee()
        {
            var data = await _context.Employee.ToListAsync();
            return data;
        }

        public async Task<Employee> GetEmployee(int id)
        {
            var data = await _context.Employee.FirstOrDefaultAsync(x => x.Id == id);
            return data;
        }

        // Generic methods
        public void Add<T>(T entity) where T : class
        {
            _context.Add(entity);
        }

        public void Delete<T>(T entity) where T : class
        {
            _context.Remove(entity);
        }

        public async Task<bool> SaveAll()
        {
            return await _context.SaveChangesAsync() > 0;
        }
    }
}

【问题讨论】:

  • 仅供参考,您应该让您的 GetAllEmployee 和 GetEmployee 像其他人一样通用。我认为您唯一的选择是编写一个 for 循环,将其映射到所需类型的集合中。手动和痛苦。

标签: c# asp.net-core dto


【解决方案1】:

您可以使用扩展方法从您的实体类型映射到您的 DTO 类型。

public static EmployeeForShortDto ToDto(this Employee employee)
{
    if (employee != null)
    {
        return new EmployeeForShortDto
        {
            Id = employee.Id,
            EmpCode = employee.EmpCode,
            Fname = employee.Fname,
            Lname = employee.Lname,
            Age = NetCoreWebApplication1.Other.Extension.CalcAge(employee.DateBirth)
        };
    }

    return null;
}

然后在需要的地方使用。

[HttpGet("{id}")]
public async Task<IActionResult> GetEmployee(int id)
{
    var data = await _repo.GetEmployee(id);

    if (data == null) 
    {
        return NotFound();
    }

    return Ok(data.ToDto());
}

[HttpGet]
public async Task<IActionResult> GetAllEmployee()
{
    var data = await _repo.GetAllEmployee();

    return Ok(data.Select(x => x.ToDto()));
}

【讨论】:

    【解决方案2】:

    感谢您的所有回复,所有这些对我都非常有用。最后,我得到了@Brad 的解决方案。我还学习了如何在将记录添加到数据库之前进行从 DTO 到类的反向映射。

    我把我的代码放在下面以防有人想看。任何 cmets/建议都非常受欢迎。谢谢。

    Extension.cs

    using NetCoreWebApplication1.Dto;
    using NetCoreWebApplication1.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace NetCoreWebApplication1.Other
    {
        public static class Extension
        {
            public static EmployeeForShortDto MapToEmployeeForShortDto(this Employee emp)
            {
                if (emp != null)
                {
                    return new EmployeeForShortDto
                    {
                        Id = emp.Id,
                        EmpCode = emp.EmpCode,
                        Fname = emp.Fname,
                        Lname = emp.Lname,
                        Age = emp.DateBirth.CalcAge()
                    };
                }
    
                return null;
            }
    
            public static EmployeeForListDto MapToEmployeeForListDto(this Employee emp)
            {
                if (emp != null)
                {
                    return new EmployeeForListDto
                    {
                        Id = emp.Id,
                        EmpCode = emp.EmpCode,
                        Fname = emp.Fname,
                        Lname = emp.Lname,
                        Age = emp.DateBirth.CalcAge(),
                        EntityCode = emp.EntityCode,
                        IsActive = emp.IsActive
                    };
                }
    
                return null;
            }
    
            public static Employee MapFromEmployeeForAddDto(this EmployeeForAddDto emp)
            {
                if (emp != null)
                {
                    return new Employee
                    {
                        EmpCode = emp.EmpCode,
                        Fname = emp.Fname,
                        Lname = emp.Lname,
                        IdCard = emp.IdCard,
                        IsActive = 1
                    };
                }
    
                return null;
            }
    
            public static int CalcAge(this DateTime? dateBirth)
            {
                if (dateBirth.HasValue)
                {
                    var age = DateTime.Today.Year - dateBirth.Value.Year;
                    if (dateBirth.Value.AddYears(age) > DateTime.Today) age--;
                    return age;
                }
                else
                {
                    return 0;
                }
            }
        }
    }
    

    MasterRepository.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.EntityFrameworkCore;
    using NetCoreWebApplication1.Dto;
    using NetCoreWebApplication1.Models;
    
    namespace NetCoreWebApplication1.Repository
    {
        public class MasterRepository : IMasterRepository
        {
            private readonly PrDbContext _context;
    
            public MasterRepository(PrDbContext context)
            {
                _context = context;
            }
    
    
            // Employee
            public async Task<List<Employee>> GetAllEmployee()
            {
                var data = await _context.Employee.ToListAsync();
                return data;
            }
    
            public async Task<Employee> GetEmployee(int id)
            {
                var data = await _context.Employee.FirstOrDefaultAsync(x => x.Id == id);
                return data;
            }
    
            public async Task<Employee> AddEmployee(Employee data)
            {
                await _context.Employee.AddAsync(data);
                await _context.SaveChangesAsync();
                return data;
            }
    
            public async Task<bool> EmployeeExists(string entityCode, string empCode)
            {
                if (await _context.Employee.AnyAsync(x =>
                    x.EntityCode == entityCode &&
                    x.EmpCode == empCode))
                    return true;
    
                return false;
            }
    
            // Generic methods
            public void Add<T>(T entity) where T : class
            {
                _context.Add(entity);
            }
    
            public void Delete<T>(T entity) where T : class
            {
                _context.Remove(entity);
            }
    
            public async Task<bool> SaveAll()
            {
                return await _context.SaveChangesAsync() > 0;
            }
        }
    }
    

    EmployeeController.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using NetCoreWebApplication1.Dto;
    using NetCoreWebApplication1.Repository;
    using NetCoreWebApplication1.Other;
    using NetCoreWebApplication1.Models;
    
    namespace NetCoreWebApplication1.Controller
    {
        [Route("api/[controller]")]
        [ApiController]
        public class EmployeeController : ControllerBase
        {
            private readonly IMasterRepository _repo;
    
            public EmployeeController(IMasterRepository repo)
            {
                _repo = repo;
            }
    
            [HttpPost("add")]
            public async Task<IActionResult> AddEmployee(EmployeeForAddDto emp)
            {
                if (await _repo.EmployeeExists(emp.EntityCode, emp.EmpCode))
                    ModelState.AddModelError("Employee", "Employee is duplicate (EntityCode + EmpCode)");
    
                if (!ModelState.IsValid)
                    return BadRequest(ModelState);
    
                Employee employeeToAdd = emp.MapFromEmployeeForAddDto();
    
                await _repo.AddEmployee(employeeToAdd);
    
                return StatusCode(201);
            }
    
    
            [HttpGet("{id}")]
            public async Task<IActionResult> GetEmployee(int id)
            {
                var data = await _repo.GetEmployee(id);
    
                if (data == null) return NotFound();
    
                return Ok(data.MapToEmployeeForShortDto());
            }
    
            [HttpGet]
            public async Task<IActionResult> GetAllEmployee()
            {
                var data = await _repo.GetAllEmployee();
    
                //var dataDto = data.Select(x => x.MapToEmployeeForShortDto());
                var dataDto = data.Select(x => x.MapToEmployeeForListDto());
    
                return Ok(dataDto);
            }
    
        }
    }
    

    【讨论】:

      【解决方案3】:

      好的,你的问题的直接答案是“做它公关返回值”;

      List<EmployeeForShortDto> result = new List<EmployeeForShortDto>();
      foreach(Employee dbEmployee in data )
      {
       result.add(new EmployeeForShortDto()
                  {
                      Id = dbEmployee .Id,
                      EmpCode = dbEmployee .EmpCode,
                      Fname = dbEmployee .Fname,
                      Lname = dbEmployee .Lname,
                      Age = NetCoreWebApplication1.Other.Extension.CalcAge(dbEmployee .DateBirth)
                  });
      }
      

      但是,这是特定于您的项目的类型。为什么不创建一个使用反射来映射对象的通用方法,或者通过附加的属性,或者直接通过属性名称? 如果你完成了,你将能够将任何对象传输到 DTO,只要你遵守属性名称的内部规则或通过属性设置映射。

      【讨论】:

      • 创建自己的通用映射器的问题在于处理复杂的模型。 “除非您打算更多地了解轮子,否则不要重新发明轮子”。 ://
      • @JohnEphraimTugado 他明确告诉我们他正在尝试这样做。他不想使用第三方映射器,他想在使用组件之前了解流程。因此,做组件正在做的事情是他正在经历的整个考验的目标。
      • 感谢您的回复,这对我很有用。是的,我想在意识到使用第 3 方的好处之前学习一个手动过程。感谢分享,伙计们。
      【解决方案4】:

      针对您的问题,以新方法提取您的实现。

      EmployeeForShortDto ConvertToDto(Employee data)
      {
       var dataDto = new EmployeeForShortDto()
              {
                  Id = data.Id,
                  EmpCode = data.EmpCode,
                  Fname = data.Fname,
                  Lname = data.Lname,
                  Age = NetCoreWebApplication1.Other.Extension.CalcAge(data.DateBirth)
              };
      }
      

      然后最后循环调用,

       foreach(Employee e in EmployeeList)
          { 
             dtoList.Add(ConvertToDto(e));
          }
      

      对于通用实现,通过反射生成 Model 和 Dto 的属性列表。然后匹配它们的类型。

      class AdapterHelper<T1, T2>
      {
          public T1 Adapt(T2 source)
          {
              T1 targetItem = Activator.CreateInstance<T1>();
              var props = typeof(T1).GetProperties();
              var targetProps = typeof(T2).GetProperties();
              foreach (var prop in props)
              {
                  foreach (var targetProp in targetProps)
                  {
                      if (prop.Name == targetProp.Name)
                      {
                          targetProp.SetValue(targetItem, prop.GetValue(source));
                          //assign
      
                      }
                  }
              }
              return targetItem;
          }
      }
      

      这是我原始答案的link

      【讨论】:

      • 听起来不错,但现在对我来说太先进了。如果可能,将尝试学习和使用它。感谢分享。
      猜你喜欢
      • 1970-01-01
      • 2016-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多