In my OData controller I'm converting my EF entities to a DTO because the entity contains many fields which just aren't used by the UI.
This question and answer (ASP.NET WebApi OData support for DTOs) shows how I can apply the query options from the OData URI to the the EF query and return DTOs. This is great, it means I'm getting the benefit of querying the DB and also the benefit of serialising smaller entities.
However, how do I then apply the Delta with my patched fields to my entity when I need to update it?
The field names in the entity don't match the DTO.
I can use the changed fields collection from the Delta but then I'd have map all the field names and use reflection to update all the properties in the Entity.
Is there a better way?
Should I use my entity instead of the DTO and use the odata $select parameter to reduce the size of data on the wire.
Should I just revert back to WebAPI and have individual update functions which only take the parameters that are needed, for example UpdateStartDate(int id, DateTime newStartDate)
I have just ran into the same problem and found the following link to be helpful: http://qimata.com/?p=1381
For the sake of prosperity, here is the code which uses AutoMapper to map the Database entity to a DTO, you then apply the Patching to the DTO objects, and then use AutoMapper to map back to the database entity before saving:
[AcceptVerbs("PATCH", "MERGE")]
public virtual async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<DtoEntity> delta, CancellationToken cancellationToken)
{
Validate(delta.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await _genericRepository.FindAsync(cancellationToken, key);
var dto = Mapper.Map<DtoEntity>(entity);
delta.Patch(dto);
Mapper.Map(dto, entity);
await _context.SaveChangesAsync(cancellationToken);
return Updated(dto);
}
One other thing worth mentioning is that when using AutoMapper with EntityFramework, be careful of automatic expansions of navigation properties.
You can disable expansions by using the ExplicitExpansion
method:
Mapper.CreateMap<DbEntity, DtoEntity>()
.ForMember(dest => dest.Example, opt => opt.ExplicitExpansion());
I also had to disable Lazy Loading with Configuration.LazyLoadingEnabled = false;
in my DbContext Constructor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With