While I am updating related records, I noticed that after updating, the primary key is reassigned. The latter is a long
datatype (RecordId
in the example below in PartnersRegistry
) and I noticed that its value has changed after saving the changes. It looks like EF is deleting the old entity object and adding the new one instead of just updating the changes (await unitOfWork.CompleteAsync();
in the example below).
I have two questions:
- Is this a normal behavior?
- What is the best
Primary Key
to be used here to avoid seeding?
I am posting the relevant code:
Entity 1
[Table("PatientsRegistry")]
public class PatientRegistry
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Display(Name = "Record Id")]
public long RecordId { get; set; }
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
[Display(Name = "Patient File Number")]
public long PatientFileId { get; set; }
public DateTimeOffset DateCreated { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public virtual ICollection<PartnerRegistry> Partners { get; set; }
}
Related entity
[Table("PartnersRegistry")]
public class PartnerRegistry
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long RecordId { get; set; }
public long PatientFileId { get; set; }
public long PartnerFileId { get; set; }
[JsonIgnore]
public virtual PatientRegistry PatientsRegistry { get; set; }
public DateTime StartDate { get; set; }
public DateTime? EndDate { get; set; }
}
In my controller
[HttpPut]
public async Task<IActionResult> UpdatePatient(long fileId, [FromBody] SavePatientsRegistryViewModel model)
{
SuccessResponse response = new SuccessResponse();
if (!ModelState.IsValid)
return StatusCode(400, StaticHandlers.FormatErrorResponse(ModelState));
PatientRegistry patient = await repository.GetPatient(fileId);
if (patient == null)
{
ModelState.AddModelError(string.Empty, "The patient does not exist in the database");
return StatusCode(404, StaticHandlers.FormatErrorResponse(ModelState));
}
model.PatientFileId = patient.PatientFileId;
PatientRegistry modelPatientRegistryData = mapper.Map<SavePatientsRegistryViewModel, PatientRegistry>(model, patient);
await unitOfWork.CompleteAsync();
response.SuccessMessage = patient.FirstName + " " + patient.LastName + " has been updated successfully!";
response.DataResponse = patient.PatientFileId;
return StatusCode(200, response);
}
Based on the comments below, I am extending the relevant code.
These are my view models that are consumed by the controller:
public class SavePatientsRegistryViewModel
{
public long PatientFileId { get; set; }
public ICollection<SavePartnerRegistryViewModel> Partners { get; set; }
public OnlineAccountViewModel OnlineAccount { get; set; }
public SavePatientsRegistryViewModel()
{
Partners = new Collection<SavePartnerRegistryViewModel>();
}
}
public class SavePartnerRegistryViewModel
{
public string RecordId { get; set; }
public long PatientFileId { get; set; }
public long PartnerFileId { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
}
And my mapping profiles are:
CreateMap<SavePatientsRegistryViewModel, PatientRegistry>()
.ForMember(pr => pr.RecordId, opt => opt.Ignore())
.ForMember(pr => pr.PatientFileId, opt => opt.Ignore())
.ForMember(pr => pr.UserId, opt => opt.Ignore())
.ForMember(pr => pr.Partners, opt => opt.MapFrom(c => c.Partners))
.ForMember(pr => pr.User, opt => opt.MapFrom(c => c.OnlineAccount));
CreateMap<SavePartnerRegistryViewModel, PartnerRegistry>()
.ForMember(pr => pr.RecordId, opt => opt.Ignore())
.ForMember(pr => pr.PatientFileId, opt => opt.Ignore())
.ForMember(pr => pr.PatientFileId, opt => opt.MapFrom(s => s.PatientFileId))
.ForMember(pr => pr.PartnerFileId, opt => opt.MapFrom(s => s.PartnerFileId))
.ForMember(pr => pr.StartDate, opt => opt.MapFrom(s => s.StartDate))
.ForMember(pr => pr.EndDate, opt => opt.MapFrom(s => s.EndDate));
Basically, what I am doing in my Update method in my controller is the following:
I check if the patient exists by calling it from Dbcontext.
I map the view model to the entity.
After mapping, values are changed, I call
await unitOfWork.CompleteAsync();
which is basicallyawait context.SaveChangesAsync();
My problem is in the related table (PartnerRegistry
). Upon saving changes, the column RecordId
, which is a PK, gets a new value. Now, EF is not updating this column for sure and even if I try, I get an error saying that values cannot be inserted in this filed since its autogenerated!
The only way RecordId
changes its value is if the whole record is inserted, or removed and re-inserted.
Example below.
Before Update:
{
"patientFileId": 1111,
"partners": [
{
"recordId": **5**,
"patientFileId": 1111,
"partnerFileId": 2222,
"startDate": "1/1/90 12:00:00 AM",
"endDate": "1/1/00 12:00:00 AM",
"patientName": null
}
]
}
After Update:
{
"patientFileId": 1111,
"partners": [
{
"recordId": **6**,
"patientFileId": 1111,
"partnerFileId": 2222,
"startDate": "1/1/90 12:00:00 AM",
"endDate": "1/1/00 12:00:00 AM",
"patientName": null
}
]
}
Update
After debugging, I found that the Partners
collection is re-initialized after mapping!
PatientRegistry patient = await repository.GetPatient(fileId);
The Partners
collection here is the same as in the context. However, after mapping, the whole ICollection
get re-initialized for some reason. Specifically after:
PatientRegistry modelPatientRegistryData = mapper.Map<SavePatientsRegistryViewModel, PatientRegistry>(model, patient);