Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update nested collection (using mapper between viewmodels and entities) #20102

Closed
influento opened this issue Feb 28, 2020 · 4 comments
Closed

Comments

@influento
Copy link

influento commented Feb 28, 2020

Hello,

First of all I'm not really sure if I chose correct category for this issue coz it's something between feature request and just a question.

Let's assume we have simple entity model.

 public class University
    {
        [Key]
        public int Id { get; set; }

        public string Name { get; set; }

        public ICollection<Student> Students { get; set; }

        public Employee()
        {
            Students = new HashSet<Student>();
        }
    }

 public class Student
    {
        [Key]
        public int Id { get; set; }

        public int UniversityId { get; set; }

        public string Name { get; set; }

        public University University { get; set; }
    }

And let's assume we already have next data in our tables:

University
Id: 1
Name: MIT

Student
Id: 1
UniversityId: 1
Name: Bob
-----------
Id: 2
UniversityId: 1
Name: Carl

Then we have an API which should update university.
And let's assume we received next model by this API

University: {
    Id: 1,
    Name: MIT,
    Students: [
        {
            Id: 1,
            Name: Bobby
        }
    ]
}

Also, let's assume that we have standard View Models for our entities so, they will looks like:

 public class UniversityVM
    {
        public int Id { get; set; }

        public string Name { get; set; }

        public ICollection<StudentVM> Students { get; set; }

        public Employee()
        {
            Students = new List<Student>();
        }
    }

 public class StudentVM
    {
        public int Id { get; set; }

        public string Name { get; set; }
    }

Those view models are mapped to our entities by some mapper for instance Automapper like:

CreateMap<StudentVM, Student>();
CreateMap<UniversityVM, University>();

Then to update it we will write next code (let's assume that we are working in disconnected scenario)

public void Update (UniversityVM request) {
    var existingUniversity = _context.Universities.Single(b => b.Id == request.Id);
    _mapper.Map(request, existingUniversity);

    _context.Update(existingUniversity);

    _context.SaveChanges();
}

In fact we replaced existingUniversity.Students with request.Students and then saved it. So, expected result should be that all the students which doesn't have specified id should be saved (it will be so), all the students which has specified id and already exists will be updated (and it will be so as well) and finally all other related students which existed before that request but, are missing in request.Students should be removed (and it won't work in this way).

If we would debug it we will find that in _context.Univerities university with id 1 will have updated collection of students so, in theory EF context knows that we removed existing students from the context.

But, why it doesn't removes those students which are not presented in updated collection anymore?

That issue makes us to implement remove operation manually what increases code to be written.

Is there any existing way to achieve "smart" update out of the box?

Smart update is when we have not write any code and by default there will be next behavior:

  1. Entities in nested collection which doesn't have specified id will be created.
  2. Existing entities in nested collection which are presented in new collection will be modified.
  3. Existing entities in nested collection which are not presented in new collection will be removed.

Thanks.

@ajcvickers
Copy link
Contributor

@Crispried If the context is already tracking all the related students, then replacing the collection with a new one should result in any students not in the new collection being marked as deleted. (Assuming the relationship is required and deleting orphans has not been disabled.)

However, if the context is not tracking the related students, then there is no way for EF to know which ones are now missing and need to be deleted. Either the related students need to be loaded, or the application needs to send appropriate information to and from the client to know which entities have been deleted. Doing this automatically is tracked by #5536. Disconnected entities documentation is here: https://docs.microsoft.com/en-us/ef/core/saving/disconnected-entities

@influento
Copy link
Author

influento commented Feb 29, 2020

@ajcvickers

So, if I got you correctly in tracking scenario it should work? But, it doesn't..

I'm receiving this error on connected scenario

The instance of entity type 'Student' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.

@ajcvickers
Copy link
Contributor

@Crispried Please attach a small runnable project or post a complete runnable code listing that demonstrates the behavior you are seeing so that we can investigate.

@influento
Copy link
Author

@ajcvickers

Sorry, I forgot to put an update in the ticket. That issue was with version 2.2.6. When I've updated it to 3.1.2 (current) it started to work as it should.

So, this ticket could be finally closed. And I hope soon we will have possibility to use update functionality with necessary behavior out of the box in disconnected scenario.

Thank you.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants