ASP.Net core web api correct way of implementing/calling update method

asp.net-core asp.net-core-mvc entity-framework-core

Question

I have two models related to each other as following:

 public class CoreGoal
    {
        [Key]
        public long CoreGoalId { get; set; }
        [Required]
        public string Title { get; set; }

        public virtual ICollection<Benefit> Benefits { get; set; }

        public CoreGoal()
        {

        }
    }

And

public class Benefit
{
    [Key]
    public long BenefitId { get; set; }
    [Required]
    public string What { get; set; }

    public long CoreGoalD { get; set; }

    [ForeignKey("CoreGoalId")]
    public virtual CoreGoal CoreGoal { get; set; }
    public Benefit()
    {

    }
}

My UPDATE method from controller class:

[Route("api/[controller]")]
    public class CoreGoalController : Controller
    {
        private readonly ICoreGoalRepository _coreGoalRepository;

        //Controller
        public CoreGoalController(ICoreGoalRepository coreGoalRepository) {
            _coreGoalRepository = coreGoalRepository;
        }

        ...

        //Update
        [HttpPut("{id}")]
        public IActionResult Update(long id, [FromBody] CoreGoal item)
        {
            if (item == null || item.CoreGoalId != id)
            {
                return BadRequest();
            }

            var coreGoal = _coreGoalRepository.Find(id);
            if (coreGoal == null)
            {
                return NotFound();
            }

            coreGoal.Title = item.Title;
            coreGoal.Benefits = item.Benefits;

            _coreGoalRepository.UpdateCoreGoal(coreGoal);
            return new NoContentResult();
        }
    }

And My Repository:

public class CoreGoalRepository : ICoreGoalRepository
{
    private readonly WebAPIDataContext _db;

    public CoreGoalRepository(WebAPIDataContext db)
    {
        _db = db;
    }

    ...

    //Find specific
    public CoreGoal Find(long key)
    {
        return _db.CoreGoals.FirstOrDefault(t => t.CoreGoalId == key);
    }

    //Update specific
    public void UpdateCoreGoal(CoreGoal coreGoal)
    {
        _db.CoreGoals.Update(coreGoal);
        _db.SaveChanges();
    }
}

My question is is it a correct way of writing UPDATE method? Consider When I did a GET request I get back:

[
  {
    "coreGoalId": 1,
    "title": "Goal 1",
    "benefits": [
      {
        "benefitId": 1,
        "what": "Benefit 1",
        "coreGoalD": 0
      }
    ]
  }
] 

Now I want to UPDATE this coregoal, so I did a PUT request as following:

enter image description here

And it gave me

An exception of type 'Microsoft.EntityFrameworkCore.DbUpdateException' occurred in Microsoft.EntityFrameworkCore.dll but was not handled in user code 

Am I sending a wrong request or I have implemented the method incorrectly either in controller or Repository? Why can't I update only partially the parent or child entity in one request?

1
1
3/24/2017 9:07:17 AM

Popular Answer

It's a bad idea to directly expose your data model as your API, and especially to accept your data model directly as parameters to your actions via model binding. Instead, use a ViewModel class that only contains the things you are showing to the client, or that you are allowing the client to update. In many cases it will make sense to flatten this model, so that it doesn't have objects containing other objects (as yours does currently).

You may run into issues if you have navigation properties going in both directions in your model, too. This can make it harder to serialize the objects, and can make it more difficult for EF to track changes. I tend to have the parent object have navigation properties to its children, but the children then only have foreign key references (int or guid properties) back to their parent. In your case this would mean removing the virtual CoreGoal property from Benefit.

You may also have a problem because your Benefit class's CoreGoalID property has a typo and is CoreGoalD.

Let me know if any of these solve your specific problem.

0
3/25/2017 12:20:24 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow