I want to update a non-id field of an object's navigation property when the user is on the form for modifying that object. Some of my "Department" table's fields are like this...
Id
DirectorId (object of Employee table)
NoOfEmployees
Location
and Employee table's fields are as follows:
Id
Name
Post
Now when the user is presented with modification of Department table, I also show the core info about the employees (with post of "Director") with current director selected in its ComboBox and all the relevant information in corresponding TextBoxes etc. As soon as the user chooses a different Director, relevant fields also change accordingly.
Now what i want is that if user changes some of the Director's information (e.g. Name or Age) at this point, the changes should reflect in DB instead of compelling the user to go to employee modification form and do changes there.
I tried the simple approach of modifying few fields of a database object but that approach is not working when the object-to-be-modified is navigation property (as discussed above). Here is the code...
using (CompanyDBContext db = new CompanyDBContext() {
int id = ((Department)cbDept.SelectedItem).Id;
Department currentDept = db.Departments.Where(x => x.Id == id).First();
Department newDept = new Department();
newDept.Id = currentDept.Id;
newDept.Employee.Id = (int)cbDir.SelectedValue; // --> has no effect;
// raises NullRefException "Obj. ref. not set to an instance of an obj."
.....;
.....;
db.Entry(currentDept).CurrentValues.SetValues(newDept);
if (dirNameChanged) {
var director = new Employee() {
Id = currentDept.DirectorId,
Name = tbDirName.Text.Trim()
};
db.Employees.Attach(director); // --> raises exception
db.Entry(director).Property(x => x.Name).IsModified = true;
}
db.SaveChanges();
}
.Attach() methods throws InvalidOperationException exception saying
Attaching an entity of type 'CompanyDatabase.Employee' failed because
another entity of the same type already has the same primary key value.
This can happen when using the 'Attach' method or setting the state of
an entity to 'Unchanged' or 'Modified' if any entities in the graph have
conflicting key values. This may be because some entities are new and have
not yet received database-generated key values. In this case use the 'Add'
method or the 'Added' entity state to track the graph and then set the state
of non-new entities to 'Unchanged' or 'Modified' as appropriate.
But then using Add() method also raises similar exception... Any work around?
P.S. Dept object is changing alright. Changing the Director object is also fine i.e. if user chooses a new Director for the Dept. Only changing the non-id fields of Director (Employee) is giving problem.
db.Employees.Attach(director); // --> raises exception
The exception message indicates that the context already contains (is tracking) Employee
object with the same PK (Id == director.Id
). So instead of creating new object (new Employee()
) you are expected to use the existing object (EF uses reference identity, i.e. does not allow two different entity instances with one and the same PK).
The standard way of doing that is to use Find
method, which will return the currently tracked object with that PK or will load it from the database. Hence the code should be something like this:
if (dirNameChanged)
{
var director = db.Employess.Find(currentDept.DirectorId);
director.Name = tbDirName.Text.Trim();
}
Note that there is no need to Attach
it or manipulating the entity/property states - the returned object is attached and tracked by the context, so any property value changes are determined automatically.