Scenario: we have a Person entity and a PersonInterest entity. The latter represents the link table, so it has a compound primary key consisting of two foreign keys. The Persons table has an autoincremented primary key.
1. We create a Person. It's ID is -1.
2. We create a PersonInterest entity in order to add an existing interest to the new person. Say, we have InterestID=1, so for the new entity we have ObjectId={-1,1}.
3. We call SaveChanges(). The Person's ID is updated to, say, 1000. The PersonInterest record is also updated, and now has a different ObjectId: {1000,1}. Note that this objects sits in the objectTable under the old key, which is {-1,1}.
4. We now search for PersonInterests. When we retrieve the new record from the table, it has a different ObjectId, so the method GetObjectForRow() creates a new entity which duplicates the old one. This is because we look for the Entity using its new ObjectId, and this key is not present in the objectTable
Proposed fix.
In ObjectContext:
private ArrayList changedObjects; //objects that have changed primary keys after saving a related object
/// <summary>
/// Adds an EntityObject to the list of objects to be processed later (updating ObjectIds)
/// </summary>
/// <param name="eo"></param>
protected internal void RegisterForUpdate(IEntityObject eo)
{
if (!this.changedObjects.Contains(eo)) this.changedObjects.Add(eo);
}
/// <summary>
/// Update Object IDs for all objects with changed primary keys (except for autoincrements)
/// </summary>
protected void UpdateObjectIds()
{
foreach (IEntityObject eo in this.changedObjects)
{
ObjectTable.RemoveObject(eo);
ObjectId newOid = new ObjectId(eo.Row.Table.TableName, this.GetPrimaryKeyValuesForRow(EntityMapFactory.GetMap(eo.Row.Table.TableName), eo.Row, DataRowVersion.Current));
ObjectTable.AddObject(newOid, eo);
}
this.changedObjects.Clear();
}
In ObjectRelationBase, at the end of public void OnColumnChanging(object sender, DataColumnChangeEventArgs e)
if ((Owner.Row.RowState == DataRowState.Added) && (eo != null) && (Array.IndexOf(e.Row.Table.PrimaryKey, e.Row.Table.Columns[foreignColumnName]) != -1))
{
object eo = ObjectForForeignRow(e.Row, false);
eo.Context.RegisterForUpdate(eo);
}