I am working on a web api project on ASP.NET Core 2.0 with Entity Framework Core connecting to a postgres database.
The following PUT method works without at problem but I feel I might be misunderstanding the use of the update method and I also feel like there should be a more elegant solution to update an existing object in a manner holding with a PUT request (not a PATCH).
// PUT: api/Discount/5
[HttpPut("{id}")]
[ProducesResponseType(204)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
[ProducesResponseType(500)]
public IActionResult Put(int id, [FromBody]Discount discount)
{
if (ModelState.IsValid)
{
using (var context = new BarberskabetDbContext())
{
if (context.Discounts.Any(x => x.DiscountId == id && !x.Deleted))
{
var dbDiscount = context.Discounts.FirstOrDefault(x => x.DiscountId == id && !x.Deleted);
dbDiscount.AppliesOnce = discount.AppliesOnce;
dbDiscount.AppliesToProductType = discount.AppliesToProductType;
dbDiscount.Code = discount.Code;
dbDiscount.DiscountType = discount.DiscountType;
dbDiscount.Duration = discount.Duration;
dbDiscount.DurationUsageLimit = discount.DurationUsageLimit;
dbDiscount.EndsAt = discount.EndsAt;
dbDiscount.OncePerCustomer = discount.OncePerCustomer;
dbDiscount.RestrictByEmail = discount.RestrictByEmail;
dbDiscount.StartsAt = discount.StartsAt;
dbDiscount.Status = discount.Status;
dbDiscount.TimesUsed = discount.TimesUsed;
dbDiscount.UsageLimit = discount.UsageLimit;
dbDiscount.Value = discount.Value;
context.Update(dbDiscount);
if (context.SaveChanges() == 0)
{
return StatusCode(500, "Unable to update record.");
}
return NoContent();
}
else
{
return NotFound();
}
}
}
else
{
return BadRequest(ModelState);
}
}
I feel like there should be a better way to put the new info into the database but I am having trouble seeing it.
EDIT: My idea of elegant/better was both less code for same effect and better use of tools as Ivan rightly suggests. Thanks to all of you that suggested Automapper to remove the tedious remapping of variables from dto to model. Thank you also especially to Ivan Stoev for explaining the workings of a forced update and a partial update and idea that it is better to do error handling with Exceptions instead of number of rows affected.
Update
method is used for so called forced update, i.e. when you have disconnected entity which you are sure is existing. It works without loading the entity from the database and simply updates all properties.
Taking your example, the forced update looks like this:
if (context.Discounts.Any(x => x.DiscountId == id && !x.Deleted))
{
context.Update(discount);
context.SaveChanges();
}
If you want to update only changed properties (if any), then you should load the database entity and use the CurrentValues.UpdateValues
method to apply the changes:
var dbDiscount = context.Discounts.FirstOrDefault(x => x.DiscountId == id && !x.Deleted);
if (dbDiscount != null)
{
context.Entry(dbDiscount).CurrentValues.SetValues(discount);
context.SaveChanges();
}
Note that in this case EF might not issue update command at all (if all property values are the same as the original), i.e. SaveChanges
can return 0
, so you shouldn't use it as indication of success. In fact you should never use it for that purpose because EF will throw exception is something goes wrong.
For more information and examples, see Saving Data - Disconnected Entities documentation topic.