如何使用 AutoMapper 将 json 请求 dto 中的 OData 枚举字符串映射到实体枚举 属性

How to use AutoMapper to map OData enum string in json request dto to entity enum property

我正在开发新的 ASP.NET Core 3.1.1 API Microsoft.AspNetCore.OData v 7.3.0、AutoMapper v9.0.0 和 Microsoft.AspNetCore.Mvc.NewtonsoftJson v3.1.1

当我使用 Postman v7.18.0;

对帐户端点进行 POST 时,出现以下错误
AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.

我在创建此问题时查看了类似问题列表,但无法找到解决方案。 在查看 google 搜索 AutoMapper OData 枚举时,我能找到的所有建议都是用...

装饰我的 dto class
[AutoMap(typeof(Account))]

... 并用 ...

装饰我的 dto 枚举属性
[JsonConverter(typeof(StringEnumConverter))]

但是,我仍然遇到错误。我找到了使用 AutoMapperProfile class 和定义为

的映射器的参考资料
CreateMap<Account, AccountModel>().ReverseMap();

但是AutoMapper v9.0.0好像没有CreateMap方法了。我的理解是,将 [AutoMap(typeof(Account))] 添加到 dto class 与在配置文件 class.

中创建地图具有相同的效果

我觉得此时此刻我正在兜圈子,所以我会联系 SO 社区。我确定这很简单,我只是没有看到它。

这是我的 POST 来自 Postman 的请求正文;

{
  "@odata.context": "https://localhost:44367/v1/$metadata#Accounts",
  "AccountName": "Test Provider",
  "AccountType": "Provider",
  "IsTaxExempt": false,
  "Status": "Active"
}

这是我的 AccountsController Post 方法;

[ODataRoute]
[Produces("application/json;odata.metadata=minimal")]
[ProducesResponseType(typeof(AccountModel), Status201Created)]
[ProducesResponseType(Status400BadRequest)]
public async Task<IActionResult> Post([FromBody] AccountModel record)
{

    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }
    record.Id = new Guid();
    var entity = _mapper.Map<Account>(record);
    _context.Add(entity);
    await _context.SaveChangesAsync();
    var createdRecord = _mapper.Map<AccountModel>(entity);
    return Created(createdRecord);

}

这是我的帐户实体 class;

public class Account : EntityBase
{
    [Required]
    [Column(TypeName = "nvarchar(50)")]
    [MaxLength(50)]
    public string AccountName { get; set; }

    public AccountTypes AccountType { get; set; }

    public bool IsTaxExempt { get; set; }

}

这是 EntityBase class;

public class EntityBase
{
    [Required]
    public Guid Id { get; set; }

    public DateTimeOffset? DateTimeCreated { get; set; } = DateTime.UtcNow;

    public DateTimeOffset? DateTimeLastModified { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    public StatusTypes Status { get; set; }

    public bool DeleteFlag { get; set; }
}

这是我的帐户 DTO class;

[Filter, Count, Expand, OrderBy, Page, Select]
[AutoMap(typeof(Account))]
public class AccountModel : BaseModel
{
    [Required]
    [MaxLength(50)]
    public string AccountName { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    public AccountTypes AccountType { get; set; }

    public bool IsTaxExempt { get; set; }
}

这是我的 BaseModel class;

[Select, Filter]
public class BaseModel
{
    public Guid Id { get; set; }
    public DateTimeOffset DateTimeCreated { get; set; } = DateTime.UtcNow;
    public DateTimeOffset DateTimeLastModified { get; set; }

    [JsonConverter(typeof(StringEnumConverter))]
    public StatusTypes Status { get; set; }

    public bool DeleteFlag { get; set; }
}

这是我的 AccountTypes 和 StatusTypes 枚举

public enum AccountTypes
{
    Customer = 0,
    Reseller = 1,
    Provider = 2,
}

public enum StatusTypes
{
    Active = 0,
    Inactive = 1,
}

有什么想法吗?

事实证明,我需要创建一个 AutoMapper MapperConfiguration 实例并将其分配给映射器。

例如,我最终放入了控制器的构造函数;

    public AccountsController(CdContext context, IMapper mapper)
    { 
        _context = context ?? throw new ArgumentNullException(nameof(context));
        _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
        var config = new MapperConfiguration(cfg => cfg.CreateMap<Account, AccountModel>().ReverseMap());
        _mapper = new Mapper(config);
    }

在我这样做之后,一切都按预期进行。

Here is a link 到有关该主题的 AutoMappers 文档。