In 1-1 relationship, if entity keys are not database generated, user has to provide key values for both principal and dependent, otherwise we get (not very helpful) exception:
Multiplicity constraint violated. The role 'ArubaRun_RunOwner_Source' of the relationship 'OneToOneNonGeneratedKeyIssue.ArubaRun_RunOwner' has multiplicity 1 or 0..1.
I would expect this to work, update pipeline should populate the dependent key values based on the values of principal. (just like it's done for database generated keys)
Issue repros for the following model:
public class ArubaOwner
{
public int Id { get; set; }
public ArubaRun OwnedRun { get; set; }
}
public class ArubaRun
{
public int Id { get; set; }
public ArubaOwner RunOwner { get; set; }
}
public class ArubaContext : DbContext
{
static ArubaContext()
{
Database.SetInitializer(new ArubaInitializer());
}
public DbSet<ArubaOwner> Owners { get; set; }
public DbSet<ArubaRun> Runs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// non-generated key
modelBuilder.Entity<ArubaOwner>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<ArubaRun>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<ArubaRun>().HasRequired(r => r.RunOwner).WithRequiredDependent(o => o.OwnedRun);
}
}
public class ArubaInitializer : DropCreateDatabaseIfModelChanges<ArubaContext>
{
private const int EntitiesCount = 10;
protected override void Seed(ArubaContext context)
{
var owners = InitializeOwners();
var runs = InitializeRuns();
for (var i = 0; i < EntitiesCount; i++)
{
owners[i].OwnedRun = runs[i];
}
for (var i = 0; i < EntitiesCount; i++)
{
runs[i].RunOwner = owners[i];
}
for (int i = 0; i < EntitiesCount; i++)
{
context.Owners.Add(owners[i]);
context.Runs.Add(runs[i]);
}
context.SaveChanges();
base.Seed(context);
}
private ArubaOwner[] InitializeOwners()
{
var owners = new ArubaOwner[EntitiesCount];
for (var i = 0; i < EntitiesCount; i++)
{
var owner = new ArubaOwner
{
Id = i,
};
owners[i] = owner;
}
return owners;
}
private ArubaRun[] InitializeRuns()
{
var runs = new ArubaRun[EntitiesCount];
for (var i = 0; i < EntitiesCount; i++)
{
var run = new ArubaRun
{
};
runs[i] = run;
}
return runs;
}
}
Stack trace:
System.Data.Entity.Core.Objects.EntityEntry.WillNotRefSteal(System.Data.Entity.Core.Objects.DataClasses.EntityReference refToPrincipal, System.Data.Entity.Core.Objects.Internal.IEntityWrapper wrappedPrincipal)
System.Data.Entity.Core.Objects.EntityEntry.FixupEntityReferenceToPrincipal(System.Data.Entity.Core.Objects.DataClasses.EntityReference relatedEnd, System.Data.Entity.Core.EntityKey foreignKey, bool setIsLoaded, bool replaceExistingRef)
System.Data.Entity.Core.Objects.EntityEntry.FixupReferencesByForeignKeys(bool replaceAddedRefs)
System.Data.Entity.Core.Objects.ObjectStateManager.FixupReferencesByForeignKeys(System.Data.Entity.Core.Objects.EntityEntry newEntry, bool replaceAddedRefs)
System.Data.Entity.Core.Objects.ObjectStateManager.FixupKey(System.Data.Entity.Core.Objects.EntityEntry entry)
System.Data.Entity.Core.Objects.EntityEntry.AcceptChanges()
System.Data.Entity.Core.Objects.ObjectContext.AcceptAllChanges()
System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(System.Data.Entity.Core.Objects.SaveOptions options)
System.Data.Entity.Core.Objects.ObjectContext.SaveChanges.AnonymousMethod__e()
System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.ProtectedExecute<int>(System.Func<int> func)
System.Data.Entity.Infrastructure.ExecutionStrategy.Execute<int>(System.Func<int> func)
System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(System.Data.Entity.Core.Objects.SaveOptions options)
System.Data.Entity.Internal.InternalContext.SaveChanges()
System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
System.Data.Entity.DbContext.SaveChanges()
Comments: __EF Team Triage:__ We reconsidered this issue but given where we are in the EF6 release and the scope/risk of these changes we aren't going to take the change. We agree that this should be improved post-EF6.
Multiplicity constraint violated. The role 'ArubaRun_RunOwner_Source' of the relationship 'OneToOneNonGeneratedKeyIssue.ArubaRun_RunOwner' has multiplicity 1 or 0..1.
I would expect this to work, update pipeline should populate the dependent key values based on the values of principal. (just like it's done for database generated keys)
Issue repros for the following model:
public class ArubaOwner
{
public int Id { get; set; }
public ArubaRun OwnedRun { get; set; }
}
public class ArubaRun
{
public int Id { get; set; }
public ArubaOwner RunOwner { get; set; }
}
public class ArubaContext : DbContext
{
static ArubaContext()
{
Database.SetInitializer(new ArubaInitializer());
}
public DbSet<ArubaOwner> Owners { get; set; }
public DbSet<ArubaRun> Runs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// non-generated key
modelBuilder.Entity<ArubaOwner>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<ArubaRun>().Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<ArubaRun>().HasRequired(r => r.RunOwner).WithRequiredDependent(o => o.OwnedRun);
}
}
public class ArubaInitializer : DropCreateDatabaseIfModelChanges<ArubaContext>
{
private const int EntitiesCount = 10;
protected override void Seed(ArubaContext context)
{
var owners = InitializeOwners();
var runs = InitializeRuns();
for (var i = 0; i < EntitiesCount; i++)
{
owners[i].OwnedRun = runs[i];
}
for (var i = 0; i < EntitiesCount; i++)
{
runs[i].RunOwner = owners[i];
}
for (int i = 0; i < EntitiesCount; i++)
{
context.Owners.Add(owners[i]);
context.Runs.Add(runs[i]);
}
context.SaveChanges();
base.Seed(context);
}
private ArubaOwner[] InitializeOwners()
{
var owners = new ArubaOwner[EntitiesCount];
for (var i = 0; i < EntitiesCount; i++)
{
var owner = new ArubaOwner
{
Id = i,
};
owners[i] = owner;
}
return owners;
}
private ArubaRun[] InitializeRuns()
{
var runs = new ArubaRun[EntitiesCount];
for (var i = 0; i < EntitiesCount; i++)
{
var run = new ArubaRun
{
};
runs[i] = run;
}
return runs;
}
}
Stack trace:
System.Data.Entity.Core.Objects.EntityEntry.WillNotRefSteal(System.Data.Entity.Core.Objects.DataClasses.EntityReference refToPrincipal, System.Data.Entity.Core.Objects.Internal.IEntityWrapper wrappedPrincipal)
System.Data.Entity.Core.Objects.EntityEntry.FixupEntityReferenceToPrincipal(System.Data.Entity.Core.Objects.DataClasses.EntityReference relatedEnd, System.Data.Entity.Core.EntityKey foreignKey, bool setIsLoaded, bool replaceExistingRef)
System.Data.Entity.Core.Objects.EntityEntry.FixupReferencesByForeignKeys(bool replaceAddedRefs)
System.Data.Entity.Core.Objects.ObjectStateManager.FixupReferencesByForeignKeys(System.Data.Entity.Core.Objects.EntityEntry newEntry, bool replaceAddedRefs)
System.Data.Entity.Core.Objects.ObjectStateManager.FixupKey(System.Data.Entity.Core.Objects.EntityEntry entry)
System.Data.Entity.Core.Objects.EntityEntry.AcceptChanges()
System.Data.Entity.Core.Objects.ObjectContext.AcceptAllChanges()
System.Data.Entity.Core.Objects.ObjectContext.SaveChangesToStore(System.Data.Entity.Core.Objects.SaveOptions options)
System.Data.Entity.Core.Objects.ObjectContext.SaveChanges.AnonymousMethod__e()
System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.ProtectedExecute<int>(System.Func<int> func)
System.Data.Entity.Infrastructure.ExecutionStrategy.Execute<int>(System.Func<int> func)
System.Data.Entity.Core.Objects.ObjectContext.SaveChanges(System.Data.Entity.Core.Objects.SaveOptions options)
System.Data.Entity.Internal.InternalContext.SaveChanges()
System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
System.Data.Entity.DbContext.SaveChanges()
Comments: __EF Team Triage:__ We reconsidered this issue but given where we are in the EF6 release and the scope/risk of these changes we aren't going to take the change. We agree that this should be improved post-EF6.