ASP.NET Core – 使用自动映射和Dapper实现的DTO未按预期工作

huangapple go评论60阅读模式
英文:

ASP.NET Core - DTO not working as expected with auto-mapper and Dapper implementation

问题

在我的ASP.NET Core 6 Web API应用程序中,我正在使用Dapper与DTO一起返回查询数据库的结果。

Response类:

public class Response<T>
{
    public T Data { get; set; }
    public bool Successful { get; set; }
    public string Message { get; set; }
    public int StatusCode { get; set; }

    public Response(int statusCode, bool success, string msg, T data, List<string> errors)
    {
        Data = data;
        Successful = success;
        StatusCode = statusCode;
        Message = msg;
    }

    public Response()
    {
    }

    public static Response<T> Success(string successMessage, T data, int statusCode = 200)
    {
        return new Response<T> { Successful = true, Message = successMessage, Data = data, StatusCode = statusCode };
    }
    public override string ToString() => JsonConvert.SerializeObject(this);
}

我有这个模型类:

[Table("employees")]
public class Employee
{
    public string EMPLOYEE_CODE { get; set; }
    public string EMPLOYEE_NAME { get; set; }
    public string EMPLOYEE_ADDR1 { get; set; }
    public string EMPLOYEE_ADDR2 { get; set; }
}

然后我创建了这个DTO:

public class EmployeeDto
{
    public string EmployeeCode { get; set; }
    public string EmployeeName { get; set; }
    public string EmployeeAddr1 { get; set; }
    public string EmployeeAddr2 { get; set; }
}

这是映射配置:

public class EmployeeMapperProfile : Profile
{
    public EmployeeMapperProfile() 
    {
        CreateMap<Employee, EmployeeDto>()
            .ForMember(
                dest => dest.EmployeeCode,
                opt => opt.MapFrom(src => src.EMPLOYEE_CODE)
            )
            .ForMember(
                dest => dest.EmployeeName,
                opt => opt.MapFrom(src => src.EMPLOYEE_NAME)
            )
            .ForMember(
                dest => dest.EmployeeAddr1,
                opt => opt.MapFrom(src => src.EMPLOYEE_ADDR1)
            )
            .ForMember(
                dest => dest.EmployeeAddr2,
                opt => opt.MapFrom(src => src.EMPLOYEE_ADDR2)
            )
            .ReverseMap();
    }
}

我使用AutoMapper进行了映射,如下所示:

public static class AutoMapperServiceExtension
{
    public static void ConfigureAutoMappers(this IServiceCollection services)
    {
        services.AddAutoMapper(typeof(EmployeeMapperProfile));
    }
}

Program.cs中注入它如下:

builder.Services.ConfigureAutoMappers();

MemoryCache:

public class CacheService : ICacheService
{
    private readonly IMemoryCache _memoryCache;

    public CacheService(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public T GetData<T>(string key)
    {
        try
        {
            T item = (T)_memoryCache.Get(key);
            return item;
        }
        catch (Exception e)
        {
            throw;
        }
    }
}

然后我有存储库代码:

public async Task<Response<IEnumerable<EmployeeDto>>> GetAllEmployeesAsync()
{
    var response = new Response<IEnumerable<EmployeeDto>>();

    using IDbConnection dbConnection = Connection;

    try
    {
        string sQuery = "SELECT * FROM employees";
        dbConnection.Open();
        var cacheData = _cacheService.GetData<IEnumerable<EmployeeDto>>(sQuery);

        if (cacheData != null)
        {
            response.Data = cacheData;
            return response;
        }

        var expirationTime = DateTimeOffset.Now.AddHours(24.0);
        cacheData = await dbConnection.QueryAsync<EmployeeDto>(sQuery);

        response.Data = cacheData;
        response.StatusCode = 200;
        response.Successful = true;

        return response;
    }
    catch (Exception ex)
    {
        response.Message = "Error occured: " + ex.Message;
        response.Successful = false;
        response.StatusCode = (int)HttpStatusCode.BadRequest;

        return response;
    }
    finally
    {
        dbConnection.Close();
    }
}

最后是控制器:

[Route("api/[controller]")]
[ApiController]
public class EmployeesController : ControllerBase
{
    private readonly IEmployeeRepository _employeeRepository;

    public EmployeesController(IEmployeesRepository employeeRepository)
    {
        _employeeRepository = employeeRepository;
    }

    [HttpGet("GetAllEmployees")]
    public async Task<ActionResult<Response<EmployeeDto>>> GetQueryEmployeeAsync()
    {
        var result = await _employeeRepository.GetAllEmployeesAsync();
        return Ok(result);
    }
}

然而,当我在Swagger中启动它时,不是每个字段都填充了数据,而是为null,如下所示。

Swagger响应:

{
  "data": [
    {
      "employeeCode": null,
      "employeeName": null,
      "employeeAddr1": null,
      "employeeAddr2": null
    },
    {
      "employeeCode": null,
      "employeeName": null,
      "employeeAddr1": null,
      "employeeAddr2": null
    }
  ],
  "successful": true,
  "statusCode": 0
}

但如果我删除DTO,并使用普通模型,它会正确填充数据。

如何纠正这个问题?

英文:

In my ASP.NET Core 6 Web API application, I am using Dapper in connection with DTO to return results of queried database.

Response class:

public class Response&lt;T&gt;
{
    public T Data { get; set; }
    public bool Successful { get; set; }
    public string Message { get; set; }
    public int StatusCode { get; set; }

    public Response(int statusCode, bool success, string msg, T data, List&lt;string&gt; errors)
    {
        Data = data;
        Successful = success;
        StatusCode = statusCode;
        Message = msg;
    }

    public Response()
    {
    }

    public static Response&lt;T&gt; Success(string successMessage, T data, int statusCode = 200)
    {
        return new Response&lt;T&gt; { Successful = true, Message = successMessage, Data = data, StatusCode = statusCode };
    }
    public override string ToString() =&gt; JsonConvert.SerializeObject(this);
}

I have this model class:

[Table(&quot;employees&quot;)]
public class Employee
{
    public string EMPLOYEE_CODE { get; set; }
    public string EMPLOYEE_NAME { get; set; }
    public string EMPLOYEE_ADDR1 { get; set; }
    public string EMPLOYEE_ADDR2 { get; set; }
}

Then I created this DTO:

public class EmployeeDto
{
    public string EmployeeCode { get; set; }
    public string EmployeeName { get; set; }
    public string EmployeeAddr1 { get; set; }
    public string EmployeeAddr2 { get; set; }
}

Here's the mapping:

public class EmployeeMapperProfile : Profile
{
    public EmployeeMapperProfile() 
    {
        CreateMap&lt;Employee, EmployeeDto&gt;()
            .ForMember(
                dest =&gt; dest.EmployeeCode,
                opt =&gt; opt.MapFrom(src =&gt; src.EMPLOYEE_CODE)
            )
            .ForMember(
                dest =&gt; dest.EmployeeName,
                opt =&gt; opt.MapFrom(src =&gt; srcEMPLOYEE_NAME)
            )
            .ForMember(
                dest =&gt; dest.EmployeeAddr1,
                opt =&gt; opt.MapFrom(src =&gt; src.EMPLOYEE_ADDR1)
            )
            .ForMember(
                dest =&gt; dest.EmployeeAddr2,
                opt =&gt; opt.MapFrom(src =&gt; src.EMPLOYEE_ADDR2)
            )
            .ReverseMap();
    }
}

I did the mapping using AutoMapper as shown here:

public static class AutoMapperServiceExtension
{
    public static void ConfigureAutoMappers(this IServiceCollection services)
    {
        services.AddAutoMapper(typeof(EmployeeProfile));
    }
}

Injected it in Program.cs as shown below:

builder.Services.ConfigureAutoMappers();

MemoryCache:

public class CacheService : ICacheService
{
    private readonly IMemoryCache _memoryCache;

    public CacheService(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }

    public T GetData&lt;T&gt;(string key)
    {
        try
        {
            T item = (T)_memoryCache.Get(key);
            return item;
        }
        catch (Exception e)
        {
            throw;
        }
    }
}

Then I have the repository code

public async Task&lt;Response&lt;IEnumerable&lt;EmployeeDto&gt;&gt;&gt; GetAllEmployeesAsync()
{
    var response = new Response&lt;IEnumerable&lt;EmployeeDto&gt;&gt;();

    using IDbConnection dbConnection = Connection;

    try
    {
        string sQuery = @&quot;SELECT * FROM employees&quot;;
        dbConnection.Open();
        var cacheData = _cacheService.GetData&lt;IEnumerable&lt;EmployeeDto&gt;&gt;(sQuery);

        if (cacheData != null)
        {
            response.Data = cacheData;
            return response;
        }

        var expirationTime = DateTimeOffset.Now.AddHours(24.0);
        cacheData = await dbConnection.QueryAsync&lt;EmployeeDto&gt;(sQuery);

        response.Data = cacheData;
        response.StatusCode = 00;
        response.Successful = true;

        return response;
    }
    catch (Exception ex)
    {
        response.Message = $&quot;Error occured: &quot; + ex.Message;
        response.Successful = false;
        response.StatusCode = (int)HttpStatusCode.BadRequest;

        return response;
    }
    finally
    {
        dbConnection.Close();
    }
}

And finally the controller:

[Route(&quot;api/[controller]&quot;)]
[ApiController]
public class EmployeesController : ControllerBase
{
    private readonly IEmployeeRepository _employeeRepository;

    public EmployeesController(IEmployeesRepository EmployeeRepository)
    {
        _employeeRepository = employeeRepository;
    }

    [HttpGet(&quot;GetAllEmployees&quot;)]
    public async Task&lt;ActionResult&lt;Response&lt;EmployeeDto&gt;&gt;&gt; GetQueryEmployeeAsync()
    {
        var result = await _employeeRepository.GetAllEmployeesAsync();
        return Ok(result);
    }
}

However when I launched it in Swagger, instead of getting each field populated with data, they are null as shown below.

Swagger response:

{
  &quot;data&quot;: [
    {
      &quot;employeeCode&quot;: null,
      &quot;employeeName&quot;: null,
      &quot;employeeAddr1&quot;: null,
      &quot;employeeAddr2&quot;: null
    },
    {
      &quot;employeeCode&quot;: null,
      &quot;employeeName&quot;: null,
      &quot;employeeAddr1&quot;: null,
      &quot;employeeAddr2&quot;: null
    }
  ],
  &quot;successful&quot;: true,
  &quot;statusCode&quot;: 0
}

But if I remove the DTO, and use the normal model, it populates the data just fine.

How do I correct this?

答案1

得分: 1

这只是一个示例。您也可以将IMapper注入到您的CacheService中。

重要的是,您必须调用_mapper.Map

英文:

Assuming this is your repository class

public class DataRepo 
{
    private readonly IMapper _mapper;

    // Inject Automapper through your constructor
    public DataRepo(IMapper mapper)
    {
         _mapper = mapper;
    }

    public async Task&lt;Response&lt;IEnumerable&lt;EmployeeDto&gt;&gt;&gt; GetAllEmployeesAsync()
    {
        // removed for brevity

        try
        {
           // removed for brevity

           // changed IEnumerable&lt;EmployeeDto&gt; to IEnumerable&lt;Employee&gt;
           var cacheData = _cacheService.GetData&lt;IEnumerable&lt;Employee&gt;&gt;(sQuery);

           if (cacheData != null)
           {
                // this is how you call the automapper
                response.Data = _mapper.Map&lt;IEnumerable&lt;EmployeeDto&gt;&gt;(cacheData);
                return response;
           }

           //UPDATED LINES
           var cacheData2 = await dbConnection.QueryAsync&lt;EmployeeDTO&gt;(sQuery);
           response.Data = cacheData2;

           // removed for brevity

           return response;
         }
         catch (Exception ex)
         {
             //your code
         }
        finally
        {
            //your code
        }
    }
}

This is just an example. You can also inject the IMapper into your CacheService.

What matters is, you have to call _mapper.Map

huangapple
  • 本文由 发表于 2023年5月24日 23:13:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76325034.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定