3

I am attempting to modify data via WebAPI using a automapped resource model on my EFCore entity class. I have managed to get GET, GET(by id), DELETE and POST working fine using this method.

Now the problem I face is with an Automapper Serialization and deserialization of 'System.Type' error message I get when trying to do a PUT with this Resource. I managed to narrow this error down my ID field which is a primary key identity field in SQL Server. Omitting this ID in the resource model makes the PUT work.

However, I require the ID field for my other GETS and DELETE so cannot omit it from the resource model.

Here is my Entity:

 [Table("BlazorWebApiDemo")] public partial class BlazorEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } [Column("First Name")] [StringLength(50)] public string FirstName { get; set; } = string.Empty; [Column("Last Name")] [StringLength(50)] public string LastName { get; set; } = string.Empty; [StringLength(50)] public string Address { get; set; } = string.Empty; } 

Here is my API Resource Model:

 public class BlazorEntityModel { public int Id { get; set; } // Commenting this out makes PUT work. public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } } 

Automapper Profile:

 public class BlazorEntityProfile : Profile { public BlazorEntityProfile() { this.CreateMap<BlazorEntity, BlazorEntityModel>() .ForMember(dest => dest.FirstName, opt => opt.NullSubstitute("")) .ForMember(dest => dest.LastName, opt => opt.NullSubstitute("")) .ForMember(dest => dest.Address, opt => opt.NullSubstitute("")) //.ForMember(src => src.Id, opts => opts.Ignore()) .ReverseMap(); } } 

Controller PUT method:

 [HttpPut("{id:int}")] public async Task<ActionResult<BlazorEntityModel>> UpdateBlazorItem(int id, BlazorEntityModel blazorEntityModel) { try { var oldBlazorEntity = await blazorRepository.GetBlazorById(id); if (oldBlazorEntity == null) return NotFound($"LetItem with the ID: {id} not found"); mapper.Map(blazorEntityModel, oldBlazorEntity); if(await blazorRepository.SaveChangesAsync()) { return mapper.Map<BlazorEntityModel>(oldBlazorEntity); } } catch (Exception e) { return StatusCode(StatusCodes.Status500InternalServerError, e); } return BadRequest(); //Error getting caught here. } 

This is a screenshot of the error I get, in swagger: enter image description here

Any ideas?

2

2 Answers 2

1
+50

Code looks fine, this issue could be on the client side; i.e. how you're serializing it from the Front end/Client while calling/sending to the backend API.

Double check thats you are using contentType:"application/json" & using JSON.stringify method to convert it to JSON string when you send it.

Look here and here similar issue


Update 2: In your blazor entity try adding a key attribute

public class BlazorEntityModel { [Key] // add this to force it to recognize it as key public int Id { get; set; } // Commenting this out makes PUT work. public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } } 

Update 3: Configure/override the serialization handling

Can you try to add the following in your global.asax

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore; GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter); 

If this works, let me know we may have to move to a DTO or VM so we trace and loops/circular refs.

Sign up to request clarification or add additional context in comments.

4 Comments

This is actually all happening before I serialize it. I haven't got a front end yet. Just the Data project (repos) and API project. This test is running in only swagger.
@RikuDas Try to add the key attribute on ID and let me know what happens
if you look at my OP you'll see that I did decorate the entity ID field with the Key attribute. Unfortunately this did not make it work.
@RikuDas try the configuration option in your global.asax above. I am running out of ideas, we may have to decouple the objects to VMs. But I am very optimistic this may work.
0

One way to handle that is to use two classes with inheritance.

public class MyObject { public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } } public class CreatedVersionOfMyObject : MyObject { public int Id { get; set; } } 

Then your controller can easily expose multiples endpoints with those entitities

 [Route("objects")] public class MyObjectControLler : Controller { [Route("{id}")] [HttpGet] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] [ProducesResponseType(typeof(CreatedVersionOfMyObject), (int)HttpStatusCode.OK)] public Task<IActionResult> GetByIdAsync(Guid id) { // Do what you have to do to retrieve the object by id } [Route("{id}")] [HttpDelete] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] [ProducesResponseType((int)HttpStatusCode.NoContent)] public Task<IActionResult> DeleteByidAsync(Guid id) { // Do what you have to do to delete the object by id } [Route("{id}")] [HttpPut] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] [ProducesResponseType(typeof(MyObject), (int)HttpStatusCode.OK)] public Task<IActionResult> PutAsync([FromRoute]Guid id, [FromBody]MyObject theModifiedObject) { // Do what you have to do to retrieve the object by id } [Route("")] [HttpPost] [ProducesResponseType((int)HttpStatusCode.NotFound)] [ProducesResponseType((int)HttpStatusCode.InternalServerError)] [ProducesResponseType(typeof(CreatedVersionOfMyObject), (int)HttpStatusCode.Created)] public Task<IActionResult> PostAsync(MyObject theModifiedObject) { // Do what you have to do to create the object and return the version with the id } } 

Finally, regarding the mapping you can also easily handle that (see original doc https://docs.automapper.org/en/stable/Mapping-inheritance.html)

CreateMap<BaseEntity, BaseDto>() .ForMember(dest => dest.SomeMember, opt => opt.MapFrom(src => src.OtherMember)); CreateMap<DerivedEntity, DerivedDto>() .IncludeBase<BaseEntity, BaseDto>(); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.