2

The entities are:

public class Department
{
    public int Id { get; set; }
    public string Name { get; set; }

    public IList<Employee> Employees { get; } = new List<Employee>();
}

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Designation { get; set; }

    public int DepartmentId { get; set; }
    public Department Department { get; set; } = null!;
}

The Employee table has these initial rows:

enter image description here

The update code using the Attach method is:

context.Attach(
new Department
{
    Name = "Testing",
    Employees =
    {
        new Employee
        {
            Name = "Ronaldo",
            Designation = "MD",
            DepartmentId = 1
        },
        new Employee
        {
            Id = 2,
            Name = "Sharapova",
            Designation = "VP",
            DepartmentId = 1
        }
    }
});

context.SaveChanges();

After the code execution, the updated screenshot of the table is:

enter image description here

The update does not work as expected, because:

  1. Employee 2, Name = "Sharapova", Designation = "VP", DepartmentId = 1 does not happen
  2. New employee is inserted with DepartmentId = 2. It should be DepartmentId = 1

I think that is wrong. Any reason for this behavior?

1 Answer 1

3

Navigation properties override FK properties.

You are creating a new Department (ID #2) and telling it to associate to a new employee. That employee's Department ID is the FK for the relationship, so whatever you try to set it to will be ignored, that employee will now be associated to this new department (#2). The same goes for associating a record for an existing employee. All EF knows is that since you specified an ID for Employee #2 (Alice) you want to associate her to this new Department. The fact that you specified a different name, there is no change tracking to tell it that you intend to move Alice to the new department and change her name.

So EF is doing exactly what it expects to based on the actions you are taking.

If you want to re-associate Alice and change her name, load the tracked Employee entity, change the name, and add to the new department. If you want Ronaldo to be associated with Department 1, then add him to that department.

Attach is not meant to group unrelated entities to make changes, use the DbSets or tracked parent entities for that.

Something that might be more in line with what you want...


var department = new Department
{
    Name = "Testing"
};
context.Departments.Add(department);  // Tell EF this is a new item.

var alice = context.Employees.First(x => x.Id == 2); // Loads and tracks Alice
alice.Name = "Sharapova"; // will be picked up with change tracking
department.Employees.Add(alice); // Associate Alice/Sharapova with new department
department.Employees.Add(new Employee
{
    Name = "Ronaldo",
    Designation = "MD"
});  // Add new employee to new department (2)
context.SaveChanges();

If you just want to rename Alice without changing her Department then remove the department.Employees.Add(alice); This will update her name when context.SaveCHanges() is called as all tracked entities are updated. If you want to add Ronaldo to Department #1, then you should load Department #2 (including employees) and add him to that department's Employees collection.

When working with collection navigation properties (I.e. department.Employees) avoid attempting to use setters. I.e. don't have code like department.Employees = new [] { new Employee {...} } etc. especially when working with existing, tracked entities. Instead, always work with Add() and Remove() to associate and disassociate related items. Setting a new collection can lead to errors and unexpected behaviour where you might intend to replace 5 existing associations with 3 current ones, but EF interprets this as attempting to insert duplicate rows since you are just overwriting the collection, not telling it to remove specific items.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.