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

asp.net-core automapper entity-framework-core json.net odata

Question

I am working on a new ASP.NET Core 3.1.1 API with Microsoft.AspNetCore.OData v 7.3.0, AutoMapper v9.0.0 and Microsoft.AspNetCore.Mvc.NewtonsoftJson v3.1.1

I am getting the following error when I make a POST to the Accounts endpoint using Postman v7.18.0;

AutoMapper.AutoMapperMappingException: Missing type map configuration or unsupported mapping.

I have reviewed the similar questions list when creating this question but was unable to find a solution. In reviewing google searches for AutoMapper OData Enums all I could find were the recommendation to decorate my dto class with...

[AutoMap(typeof(Account))]

... and to decorate my dto enum properties with ...

[JsonConverter(typeof(StringEnumConverter))]

However, I still get the error. I found references to using an AutoMapperProfile class with a mapper defined as

CreateMap<Account, AccountModel>().ReverseMap();

But it appears that AutoMapper v9.0.0 no longer has a CreateMap method. My understanding was that adding the [AutoMap(typeof(Account))] to the dto class had the same effect as creating the map in the profile class.

I feel like I am going in circles at this point here so I though I would reach out to the SO community. I am sure it is something simple, I am just not seeing it.

Here is my POST request body from Postman;

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

Here is my AccountsController Post method;

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

}

Here is my Account entity 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; }

}

Here is the 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; }
}

Here is my Account 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; }
}

Here is my 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; }
}

And here are my Enums for AccountTypes and StatusTypes

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

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

Any ideas?

1
0
2/14/2020 4:41:00 PM

Accepted Answer

It turns out that I needed to create an instance of an AutoMapper MapperConfiguration and assign it to the mapper.

I ended up putting in in the constructor of the Controller, for example;

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

After I did this, everything worked as expected.

Here is a link to AutoMappers docs on the subject.

0
2/20/2020 7:01:32 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow