Как вручную отобразить DTO БЕЗ использования AutoMapper? - PullRequest
0 голосов
/ 24 августа 2018

Я изучаю C # .NET Core и пытаюсь создать DTO-отображение без использования AutoMapper, так как я работаю над одним небольшим проектом и хочу понять основы перед использованием дополнительных пакетов, что удивительно, я не мог легко найти ответ в stackoverflow.com или я могу использовать неправильный поиск по ключевым словам.

Кстати, ниже приведен мой код, который я успешно сопоставил с EmployeeForShortDto в методе GetEmployee.К сожалению, я не знаю, как отобразить его в 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;
        }
    }
}

Ответы [ 4 ]

0 голосов
/ 24 августа 2018

спасибо за все ответы, все очень полезны для меня. Наконец, я получаю решение от @Brad Я также узнал, как сделать обратное отображение из DTO в класс перед добавлением записи в базу данных.

Я поставил свой код ниже на тот случай, если кто-то захочет увидеть. Любые комментарии / предложения приветствуются. Спасибо.

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);
        }

    }
}
0 голосов
/ 24 августа 2018

Для вашей проблемы извлеките вашу реализацию в новый метод.

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));
    }

Для общей реализации, создайте список свойствМодель и Дто через отражение.и затем сопоставьте их типы.

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;
    }
}

Это ссылка на мой первоначальный ответ.

0 голосов
/ 24 августа 2018

Вы можете использовать метод расширения для сопоставления вашего типа сущности с вашим типом 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()));
}
0 голосов
/ 24 августа 2018

Ладно, прямой ответ на ваш вопрос: "сделайте это или верните значения";

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, если вы придерживаетесь внутренних правил имен свойств или настраивает сопоставления через атрибуты.

...