-1

In my API project I have Course model such:

 public class Course
 {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int CourseId { get; set; }
        public string CourseName { get; set; }
        [ForeignKey("Department")]
        public int DepartmentId { get; set; }
        public Department Department { get; set; }
        public ICollection<CourseEnrollment> CourseEnrollments { get; set; }
        public ICollection<CourseAssignment> CourseAssignments { get; set; }
 }

I want to applied the DTO and AutoMapper concepts to my update course action using HttpPut, so I created a CourseUpdateDto DTO such:

public class CourseUpdateDTO
{
     [Required]
     public int CourseId { get; set; }
     [Required, MaxLength(100)]
     public string CourseName { get; set; }
     [Required]
     public int DepartmentID { get; set; }
}

This is my AutoMapper configuration:

 CreateMap<CourseUpdateDTO, Course>()
          .ForMember(dest => dest.CourseId,
                     opts => opts.MapFrom(src => src.CourseId))
          .ForMember(dest => dest.CourseName,
                     opts => opts.MapFrom(src => src.CourseName))
          .ForMember(dest => dest.DepartmentId,
                     opts => opts.MapFrom(src => src.DepartmentID));

And finally this is my trial to update course:

[HttpPut("{id:int}")]
public ActionResult PutCourse(int id, [FromBody] CourseUpdateDTO courseDto)
{
       if (courseDto is null)
       {
           return BadRequest();
       }

       var entity = new Course
       {
            CourseId = id,
            CourseName = courseDto.CourseName,
            DepartmentId = courseDto.DepartmentID
       };

       _mapper.Map(courseDto, entity);

       try
       {
           _unitOfWork.SaveChangesAsync();
       }
       catch (DbUpdateConcurrencyException)
       {
           ModelState.AddModelError(nameof(PutCourse), "unable to save changes");
           return StatusCode(500, ModelState);
       }

       return NoContent();
}

When I tested this action using Postman, it returned status of 204 But the course has not changed. Am I wrong anywhere? Im new to this concept so hope someone configure that out to me.

frankiie
  • 456
  • 1
  • 6
  • 19
  • 1
    EF has no idea that you changed `Course` since it never got tracked by EF. You need to either do `_context.Entity(entity).Status = Status.Modified;` (not exactly like that, but similar enough) or you need to select the course and then update it. Notice that the use of automapper is pointless in the code posted as you have manually mapped the entity before – Camilo Terevinto Aug 15 '21 at 07:45
  • @CamiloTerevinto I got the your idea. But in my project I use the Repository and UnitOfwork pattern. How can I do `_context.Entity(entity).Status = Status.Modified;` in the action? – frankiie Aug 15 '21 at 08:17
  • Can you please show the code for `_unitOfWork.SaveChangesAsync();`? – Camilo Terevinto Aug 15 '21 at 08:23
  • In the `IUnitOfWork` I have ` Task SaveChangesAsync();` and in `UnitOfWork` the implementation is `public async Task SaveChangesAsync() => await _context.SaveChangesAsync();` – frankiie Aug 15 '21 at 08:29
  • So that's the problem. You're saving changes asynchronously and not waiting for that to finish. You need to use async and await in `PutCourse` too. – Camilo Terevinto Aug 15 '21 at 09:15
  • This is almost a duplicate of https://stackoverflow.com/questions/50457007/in-memory-database-doesnt-save-data/50457230#50457230. Please read my answer in full – Camilo Terevinto Aug 15 '21 at 09:16
  • @CamiloTerevinto yeah, I've read your answer on that post. Thank you for the warm guide! However I read a comment, you've noticed that `that pattern (UoW + Repository) is a waste in Entity Framework Core, it's already implemented and you win nothing by re-implementing it`. Could you explain why is that? Or if not please give me a resource to read about that? Im new to EF and ASP.Net core so I'm very curious! Thanks in advanced! – frankiie Aug 16 '21 at 00:48
  • Well, the thing is that old school teachers tend to teach that UoW+Repository is the best solution for everything, while it's obviously not the case. You should only implement that if you really plan on doing something like getting rid of EF Core at some point, otherwise you just make the code much more difficult to read and, in cases like this, prone to weird errors/problems – Camilo Terevinto Aug 16 '21 at 07:54

3 Answers3

0

You didn't call the update method before saving. After mapping call update method and after call SaveChangesAsync();

0

First of all, you need to get an exact record from your database (here I mean get it from DbContext), then map incoming data to your entity.

To make it works, you have to apply some changes like below :

var entity=_context.Cources.SingleOrDefault(x=>x.Id=id);

// check if it is not null 

_mapper.Map(courseDto, entity);
 _context.Entity(entity).Status = Status.Modified;
_context.SaveChangesAsync();
osman Rahimi
  • 1,427
  • 1
  • 11
  • 26
  • I've tried your suggestion, but it still doesn't work! It seems EF Core have no idea that the `Course` will be changed since it never got tracked some thing such `_context.Entity(entity).Status = Status.Modified;`. But in my project, The `Repostory ` and `UnitOfWork` is applied. So I don't know how to do that. – frankiie Aug 15 '21 at 08:20
  • update the code in your question according to this changed you applied. – osman Rahimi Aug 15 '21 at 08:29
  • @K_ng I updated my answer, I guess you have two different `context` objects, and one that you call `saveChanges` does not have a track of the `cource` entity. – osman Rahimi Aug 15 '21 at 08:31
0

After got some suggestions from people in this post. I've changed the original action

[HttpPut("{id:int}")]
public ActionResult PutCourse(int id, [FromBody] CourseUpdateDTO courseDto)
{
       if (courseDto is null)
       {
           return BadRequest();
       }

       var entity = new Course
       {
            CourseId = id,
            CourseName = courseDto.CourseName,
            DepartmentId = courseDto.DepartmentID
       };

       _mapper.Map(courseDto, entity);

       try
       {
           _unitOfWork.SaveChangesAsync();
       }
       catch (DbUpdateConcurrencyException)
       {
           ModelState.AddModelError(nameof(PutCourse), "unable to save changes");
           return StatusCode(500, ModelState);
       }

       return NoContent();
}

to this

[HttpPut("{id:int}")]
        public async Task<ActionResult> PutCourse(int id, [FromBody] CourseUpdateDTO courseDto)
        {
            if (courseDto is null)
            {
                return BadRequest();
            }

            var entity = _unitOfWork.CourseService.Get(id);
            if (entity is null)
            {
                return NotFound();
            }

            _mapper.Map(courseDto, entity);
            try
            {
                _unitOfWork.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                ModelState.AddModelError(nameof(PutCourse), "unable to save changes");
                return StatusCode(500, ModelState);
            }

            return NoContent();
        }

However, it still doesn't work. But I realized the problem might be in the _unitOfWork.SaveChangesAsync(); that used async/await. but I didn't define the function as async. So I changed the function to async and add await await _unitOfWork.SaveChangesAsync();. Finally, it worked! Very thanks for your warm support!

frankiie
  • 456
  • 1
  • 6
  • 19