Reputation: 11
I'm building a .NET 5 API restful and I'm facing some issues.
I would like to know if is possible to update only fields that I send on JSON.
e.g.:
JSON on Postman
{
"id": 14,
"name": "Pillar 1"
}
My Model:
public class Pillar
{
[FromRoute]
public int ID { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public int IDEvaluationType { get; set; }
[ForeignKey("IDEvaluationType")]
public EvaluationType EvaluationType { get; set; }
public ICollection<Competence> Competences { get; set; }
}
My APIController
[HttpPut("{id}")]
public async Task<IActionResult> PutPillar(int id, Pillar pillar)
{
if (id != pillar.ID)
{
return BadRequest();
}
_context.Entry(pillar).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PillarExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
But when request and debug on VS, all data that I did not send, is setting with null or 0:
And that is updating my database with null values, losing my data that I did not send on JSON. I think the Entity is building a query like this:
update Pillar set ID = 14, Name = 'Pillar 123', IsActive = null, IDEvaluationType = null where ID = 14
But what a need is a query like this:
update Pillar set Name = 'Pillar 123' where ID = 14
Is there a way to update only the fields sent on JSON?
Any questions let me know.
Upvotes: 0
Views: 2600
Reputation: 43870
The only way to do this is like this:
Modify your class or create viewModel:
public class Pillar
{
[FromRoute]
public int ID { get; set; }
public string? Name { get; set; }
public bool? IsActive { get; set; }
[Required]
public int? IDEvaluationType { get; set; }
[ForeignKey("IDEvaluationType")]
public EvaluationType EvaluationType { get; set; }
public ICollection<Competence> Competences { get; set; }
}
Try this code
public async Task<IActionResult> PutPillar(int id, Pillar pillar)
{
if (id != pillar.ID)
{
return BadRequest();
}
var existed = _context.Pillars.FirstOrDefault(i=>i.ID=pillar.ID);
if(pillar.Name!=null) existed.Name=pillar.Name;
if(pillar.IsActive!=null) existed.IsActive=pillar.IsActive;
.... and so on
_context.Entry(existed).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PillarExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
for more complicated models you can init default constructor:
public Pillar()
{
Name="NotAssigned";
}
and use it like this
if(pillar.Name!="NotAssigned") existed.Name=pillar.Name;
Upvotes: 0
Reputation: 2017
It is possible to change only a subset of fields of some particular object, and the operation is known as "patching". For REST services you have to use PATCH instead of PUT, because PUT always means a change of a whole object.
There is an article on Microsoft website, how to implement PATСH requests handling, please have a look: https://learn.microsoft.com/en-us/aspnet/core/web-api/jsonpatch?view=aspnetcore-5.0
Upvotes: 1
Reputation: 1
I could have commented but I don't have enough privileges.
Yes, it is possible. just create a DTO (data transfer object) to only update certain fields example.
The Model
<!-- language-all: c# -->
public class PillarNameUpdateDTO {
[FromRoute]
public int ID { get; set; }
public string Name { get; set; }
}
The controller
<!-- language-all: c# -->
[HttpPut("{id}/name")]
public async Task<IActionResult> PutPillar(int id, PillarNameUpdateDTO dto)
{
if (id != dto.ID)
{
return BadRequest();
}
// Get the pillar
var pillar = _context.Pillars.Find(dto.ID);
if (pillar == null) {
// throw error,
return NotFound();
}
pillar.Name = dto.Name;
_context.Update(pillar);
_context.SaveChanges();
return NoContent();
}
Upvotes: 0