问题
With EF Core I am creating a principal record, but when adding and saving a dependent record it will attempt to create a new duplicate principal.
I've seen various solutions to this, but I'd like to know why this is happening to make a proper fix.
The entity models are in this fashion.
public class Company
{
public string ID { get; set; }
public string Name { get; set; }
}
public class Employee
{
public string ID { get; set; }
public string Title { get; set; }
public string Name { get; set; }
public string CompanyID { get; set; }
[ForeignKey("CompanyID")]
public Company Company { get; set; }
}
A DbContext
is created to expose with of these as a DbSet
.
public class OrganizationContext : DbContext
{
public DbSet<Company> Companies { get; set; }
public DbSet<Employee> Employees { get; set; }
// ...
}
When passing in a full object from a View to add an Employee to a Company via OrganizationContext
, it will attempt to create a new Company record as well.
var comp = new Company()
{
comp.ID = "1",
comp.Name = "The Daily Planet"
};
var emp = new Employee()
{
emp.ID = "00123",
emp.Title = "Supervisor",
emp.Name = "Clark Kent",
emp.CompanyID = "1",
emp.Company = comp
};
_context.Employees.Add(emp);
_context.SaveChanges();
If I set the Company attribute to be unchanged, it still attempts to insert a new Company.
_context.Employees.Add(emp);
_context.Entry(emp.Company).State = EntityState.Unchanged;
_context.SaveChanges();
If I set emp.Company
to null
, it won't attempt to add a new Company
emp.Company = null;
_context.Employees.Add(emp);
Is there a correct way to setup model relationships or a structure to add an Employee to a Company without needing to manually alter the object graph before saving?
回答1:
It's because you are using Add
method, which by definition cascades the Add
operation to each reachable entity:
If you create several new related entities, adding one of them to the context will cause the others to be added too.
If you were using entities with auto generated PKs, the correct way would be to use Update
method as explained in the Mix of new and existing entities:
With auto-generated keys, Update can again be used for both inserts and updates, even if the graph contains a mix of entities that require inserting and those that require updating
followed by an example and then:
Update will mark any entity in the graph, blog or post, for insertion if it does not have a key value set, while all other entities are marked for update.
Unfortunately your case is different. There is no universal out of the box method for performing the add or update. One solution is to not set the navigation property, but only the FK property (which you luckily have):
//var comp = new Company()
//{
// comp.ID = "1",
// comp.Name = "The Daily Planet"
//};
var emp = new Employee()
{
emp.ID = "00123",
emp.Title = "Supervisor",
emp.Name = "Clark Kent",
emp.CompanyID = "1",
//emp.Company = comp
};
_context.Employees.Add(emp);
_context.SaveChanges();
Another way which will cost you a db roundtrip is to resolve the related existing entity from the context:
var comp = _context.Companies.Find("1");
var emp = new Employee()
{
emp.ID = "00123",
emp.Title = "Supervisor",
emp.Name = "Clark Kent",
//emp.CompanyID = "1", // Not needed, but won't hurt if you include it
emp.Company = comp
};
_context.Employees.Add(emp);
_context.SaveChanges();
The last option is to use TrackGraph and decide individually for each entity.
来源:https://stackoverflow.com/questions/53544256/saving-dependent-record-without-creating-a-duplicate-principal-record