Here is the approx. code I am working with.
public class Note {
public virtual Customer Customer { get; set; }
public virtual User User { get; set; }
public ICollection<NoteComment> Comments { get; set; }
}
public class NoteComment {
public virtual User User { get; set; }
}
public class User {
public ICollection<Note> Notes { get; set; }
}
public class Customer {}
// --------------------------------------
public class OurDataContext {
private void ConfigureNotes(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Note>()
.HasRequired<User>(n => n.User)
.WithMany(u => u.Notes)
.Map(a => a.MapKey("UserId"));
modelBuilder.Entity<Note>()
.HasRequired(n => n.Customer)
.WithMany(c => c.Notes)
.Map(a => a.MapKey("idCustomer"));
modelBuilder.Entity<Note>()
.HasMany(n => n.Comments)
.WithRequired()
.HasForeignKey(c => c.NoteID);
/*
modelBuilder.Entity<NoteComment>()
.HasRequired<User>(c => c.User)
.WithMany()
.Map(a => a.MapKey("UserId"));
*/
}
}
}
Note that in the ConfigureNotes() method, the last configuration is commented out. If I leave this commented out, EF will create my tables just fine, but if I uncomment this block, I get the following error:
Unhandled Exception: System.InvalidOperationException: The database creation succeeded, but the creation of the database objects did not. See inner exception for more details. ---> System.Data.SqlServerCe.SqlCeException: The referential relationship will result in a cyclical reference that is not allowed. [ Constraint name = Note_Comments ]
at System.Data.SqlServerCe.SqlCeCommand.ProcessResults(Int32 hr)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommandText(IntPtr& pCursor, Boolean& isBaseTableCursor)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteCommand(CommandBehavior behavior, String method, ResultSetOptions options)
at System.Data.SqlServerCe.SqlCeCommand.ExecuteNonQuery()
at System.Data.SqlServerCe.SqlCeProviderServices.DbCreateDatabase(DbConnection connection, Nullable`1 timeOut, StoreItemCollection storeItemCollection)
--- End of inner exception stack trace ---
...
What I don’t understand is why the navigation property from NoteComment => User is generating a circular reference between Note => NoteComment.
EDIT
For some reason, specifying the FK in the NoteComment class as a nullable property fixed the problem.
public class NoteComment {
public Guid? UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
}
Then I removed the commented out mapping code in the data context class.
This is not ideal, but I can manage this constraint manually.
SQL Server is very conservative about possible circular references or multiple delete paths compared to other databases.
Yours is originating from multiple delete paths to NoteComment:
One solution is to remove the Cascade On Delete for User -> NoteComment and do the cleanup manually.
You can also write a database trigger to do the cleanup. Here’s an example trigger:
Edits – additional information:
If you don’t have it already, I highly suggest the EF Power Tools extension. This gives you the ability to right click on any class implementing DbContext and get the Entity Framework context menu –
Right click your DbContext class in Solution Explorer -> Entity Framework -> View DDL SQL
That will give you the Sql statement used to generate your entire data model – very useful indeed to see what exactly EF thinks it’s building. You can try and run it manually in SqlServer and get a bit closer to the errors that it’s running into. When EF is building up the DDL Sql, short of compile errors it will usually get you something (or an entirely cryptic null reference error – then check your Output window), but that something might not run in SqlServer.
Also, you can manually remove the cascade on delete for one to many relationships with the fluent configuration, no need to specify keys unless you want that property: