英文:
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<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);
}
I have this model class:
[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; }
}
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<Employee, EmployeeDto>()
.ForMember(
dest => dest.EmployeeCode,
opt => opt.MapFrom(src => src.EMPLOYEE_CODE)
)
.ForMember(
dest => dest.EmployeeName,
opt => opt.MapFrom(src => srcEMPLOYEE_NAME)
)
.ForMember(
dest => dest.EmployeeAddr1,
opt => opt.MapFrom(src => src.EMPLOYEE_ADDR1)
)
.ForMember(
dest => dest.EmployeeAddr2,
opt => opt.MapFrom(src => 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<T>(string key)
{
try
{
T item = (T)_memoryCache.Get(key);
return item;
}
catch (Exception e)
{
throw;
}
}
}
Then I have the repository code
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 = 00;
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();
}
}
And finally the controller:
[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);
}
}
However when I launched it in Swagger, instead of getting each field populated with data, they are null as shown below.
Swagger response:
{
"data": [
{
"employeeCode": null,
"employeeName": null,
"employeeAddr1": null,
"employeeAddr2": null
},
{
"employeeCode": null,
"employeeName": null,
"employeeAddr1": null,
"employeeAddr2": null
}
],
"successful": true,
"statusCode": 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<Response<IEnumerable<EmployeeDto>>> GetAllEmployeesAsync()
{
// removed for brevity
try
{
// removed for brevity
// changed IEnumerable<EmployeeDto> to IEnumerable<Employee>
var cacheData = _cacheService.GetData<IEnumerable<Employee>>(sQuery);
if (cacheData != null)
{
// this is how you call the automapper
response.Data = _mapper.Map<IEnumerable<EmployeeDto>>(cacheData);
return response;
}
//UPDATED LINES
var cacheData2 = await dbConnection.QueryAsync<EmployeeDTO>(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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论