The main reason for this question is the change from AutoMapper to Mapperly due to latest being faster.
The problem I have is that I don’t know how to automatically map UpdateBarDTO to Bar, ignoring the id of Bar when mapping so that it modifies the proper database object without giving the error:
System.InvalidOperationException: The property 'Bar.Id' is part of a key and so cannot
be modified or marked as modified. To change the principal of an existing entity with an
identifying foreign key, first delete the dependent and invoke 'SaveChanges',
and then associate the dependent with the new principal.
public record class UpdateBarDTO
(
[Required][StringLength(255)] string Name,
[Required] string Address,
[Required] string PhoneNumber,
[Required] string Email,
string Website,
string CuisineType,
[Required] string OpeningHours,
DateTime CreationDate
);
public class Bar
{
public int Id { get; set; }
public required string Name { get; set; }
public required string Address { get; set; }
public required string PhoneNumber { get; set; }
public required string Email { get; set; }
public string? Website { get; set; }
public string? CuisineType { get; set; }
public required string OpeningHours { get; set; }
public DateTime? CreationDate { get; set; }
}
I have managed to find a solution by using the following:
Use the UpdateBarFromDto() to set the id so that the database knows which object to update
[Mapper]
public partial class BarMapper
{
public partial Bar ToUpdateBar(UpdateBarDTO bar);
public Bar UpdateBarFromDto(UpdateBarDTO updateBarDTO, Bar bar)
{
var dto = ToUpdateBar(updateBarDTO);
dto.Id = bar.Id;
return dto;
}
Payload: only the name changes
PUT <http://localhost:5294/bars/1>
Content-Type: application/json
{
"Name": "Sunset Lounge Skyy",
"Address": "123 Ocean Drive, Miami, FL 33139",
"PhoneNumber": "305-555-1234",
"Email": "[email protected]",
"Website": "<http://www.sunsetlounge.com>",
"CuisineType": "Seafood",
"OpeningHours":"Monday: 10:00 AM - 11:00 PM,Tuesday: 10:00 AM - 11:00 PM,Wednesday: 10:00 AM - 11:00 PM,Thursday: 10:00 AM - 11:00 PM,Friday: 10:00 AM - 12:00 AM,Saturday: 10:00 AM - 12:00 AM,Sunday: 10:00 AM - 10:00 PM",
"CreationDate": "2020-08-15T00:00:00"
}
Controller
//PUT /bars/1
group.MapPut("/{id}", async (int id, UpdateBarDTO updatedBar, ServitusDbContext dbContext) =>
{
var existingBar = await dbContext.Bars.FindAsync(id);
if (existingBar is null)
return Results.NotFound();
//set the id of updatedBarDTO
var bar = new BarMapper().UpdateBarFromDto(updatedBar, existingBar);
//locate the bar you want to update, map it to DTO and set the values
dbContext.Entry(existingBar)
.CurrentValues
.SetValues(bar);
await dbContext.SaveChangesAsync();
return Results.NoContent();
});
For those who know AutoMapper I want to achieve the same as this line:
var bar = mapper.Map<UpdateBarDTO, Bar>(updatedBar, opt => opt.AfterMap((src, dest) => dest.Id = id));
Many thanks!