I created a modal for my project. This is my first time creating a full CRUD using C# and Entity Framework Core. Create, read and delete are all working fine but I have been stuck for hours on the Update.
I have worked the problem out via the debugger and it updates exactly how I expect it to, however the issue is thrown at the .CommitAsync();
every time even though the data is updated just like it should!
The error I keep getting back is:
Screenshot of the modal for context:
Service:
public async Task<bool> UpdateUnitOfMeasure(UnitOfMeasureViewModel view, string userId)
{
var UOM = await _unitOfMeasureRepo.GetSingleAsync(uom => uom.Id == view.Id);
_mapper.Map<UnitOfMeasureViewModel, UnitOfMeasure>(view, UOM);
UOM.UpdateUserId = userId;
_unitOfMeasureRepo.Update(UOM);
foreach(var uos in view.UnitSize)
{
UnitOfMeasureSize unitSize = new UnitOfMeasureSize
{
Id = _idService.GetIdString(),
UnitSize = uos,
UnitOfMeasureId = UOM.Id,
UpdateUserId = userId,
TenantId = TenantId,
};
_unitOfMeasureSizeRepo.Update(unitSize);
}
await _unitOfMeasureRepo.CommitAsync();
return true;
}
Controller:
[HttpPut("{id}")]
[Authorize(Permissions.Admin.UnitOfMeasure.Update)]
public async Task<ActionResult> Put(string id, [FromBody] UnitOfMeasureViewModel value)
{
var retObj = await _service.UpdateUnitOfMeasure(value, await _accountService.GetUserIdFromPrincipal(User));
return new OkObjectResult(this._resp.packageResponse(retObj));
}
TSX:
private saveUOMLogic = () => {
if (!this.state.editing){
this.adminStore.saveUnitOfMeasure(this.state.unitOfMeasure)
.then(
(data: any) => {
if (data) {
const message = 'Changes have been saved';
this.MessageStore.addMessage(
message,
'success'
);
this.adminStore.getUnitOfMeasures();
}
},
(error: any) => {
this.MessageStore.addMessage(
error.toString(),
'there was an erorr in saving UOM'
);
}
);
} else {
this.adminStore.putUnitOfMeasure(this.state.unitOfMeasure)
.then(
(data: any) => {
if (data) {
const message = 'Changes have been saved';
this.MessageStore.addMessage(
message,
'success'
);
this.adminStore.getUnitOfMeasures();
}
},
(error: any) => {
this.MessageStore.addMessage(
error.toString(),
'there was an erorr in saving UOM'
);
}
);
}
}
Entities:
[Table("UnitOfMeasure")]
public class UnitOfMeasure : IEntityBase, IAuditBase
{
[Key]
[Column("UnitOfMeasureId")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
[Required]
[ForeignKey("TenantId")]
public string TenantId { get; set; }
[JsonIgnore]
public virtual Tenant Tenant { get; set; }
public string Name { get; set; }
public virtual IEnumerable<UnitOfMeasureSize> UnitOfMeasureSizes { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
[StringLength(255)]
public string CreateUserId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime UpdateDate { get; set; }
[StringLength(255)]
public string UpdateUserId { get; set; }
}
[Table("UnitOfMeasureSize")]
public class UnitOfMeasureSize : IEntityBase, IAuditBase
{
[Key]
[Column("UnitOfMeasureSize")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
[Required]
[ForeignKey("TenantId")]
public string TenantId { get; set; }
[JsonIgnore]
public virtual Tenant Tenant { get; set; }
[Required]
[ForeignKey("UnitOfMeasureId")]
public string UnitOfMeasureId { get; set; }
public virtual UnitOfMeasure UnitOfMeasure { get; set; }
[Required]
public int UnitSize { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
[StringLength(255)]
public string CreateUserId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime UpdateDate { get; set; }
[StringLength(255)]
public string UpdateUserId { get; set; }
}
CodePudding user response:
Based on your model, I do not see how do you plan to update UnitOfMeasureSize
, only Insert or Delete can be done here if view.UnitSize
is just int
.
Here code with should work without repositories. If it works, try to map to your abstraction.
public async Task<bool> UpdateUnitOfMeasure(UnitOfMeasureViewModel view, string userId)
{
var UOM = await _context.UnitOfMeasure
.Include(om => om.UnitOfMeasureSizes) // important
.FirstAsync(om => uom.Id == view.Id));
UON.TenantId = TenentId;
UON.Name = view.Name;
UON.UpdateDate = DateTime.Now;
UON.UpdateUserId = userId;
foreach(var uos in view.UnitSize)
{
var unitSize = UON.UnitOfMeasureSizes.FirstOrDefault(ms => ms.UnitSize == uos);
if (unitSize == null)
{
unitSize = new UnitOfMeasureSize
{
Id = _idService.GetIdString(),
CreateUserId = userId,
TenantId = TenantId,
UnitSize = uos
};
UON.UnitOfMeasureSizes.Add(unitSize);
}
}
await _context.SaveChangesAsync();
return true;
}
CodePudding user response:
I found a solution to this problem though I do think @svyatosiav's answer would also work.
Unit Sizes have a foreign key relationship with Unit of Measure. Since I am doing this in a service and not in a db context, I needed to commit the changes. I was running into the issue that before I would CommitAsync(), unit sizes would update with the UOM repo, then again when I mapped through the unit sizes. Therefore it was updating twice and hence the error, Unit Sizes were already updated so it couldn't be updated twice. Here is my solution:
public async Task<bool> UpdateUnitOfMeasure(UnitOfMeasureViewModel view, string userId)
{
var UOM = await _unitOfMeasureRepo.GetSingleAsync(uom => uom.Id == view.Id);
this._unitOfMeasureSizeRepo.DeleteWhere(uos => uos.UnitOfMeasureId == UOM.Id);
_mapper.Map<UnitOfMeasureViewModel, UnitOfMeasure>(view, UOM);
UOM.UpdateUserId = userId;
_unitOfMeasureRepo.Update(UOM);
foreach(var uos in view.UnitSize)
{
UnitOfMeasureSize unitSize = new UnitOfMeasureSize
{
Id = _idService.GetIdString(),
UnitSize = uos,
UnitOfMeasureId = UOM.Id,
UpdateUserId = userId,
TenantId = TenantId,
};
_unitOfMeasureSizeRepo.Add(unitSize);
}
await _unitOfMeasureRepo.CommitAsync();
return true;
}
By deleting the Unit Sizes, I could then just map and re-add the new unit sizes without an issue.