这是约。我正在使用的代码。
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"));
*/
}
}
}
请注意,在ConfigureNotes()
方法中,最后一个配置已被注释掉。如果我把这个注释掉,EF会很好地创建我的表,但是如果我取消注释这个块,我会收到以下错误:
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 ---
...
我不明白为什么导航属性来自NoteComment
=&gt; User
正在Note
=&gt;之间生成循环引用NoteComment
。
EDIT
出于某种原因,将NoteComment
类中的FK指定为可空属性可以解决问题。
public class NoteComment {
public Guid? UserId { get; set; }
[ForeignKey("UserId")]
public virtual User User { get; set; }
}
然后我删除了数据上下文类中注释掉的映射代码。
这不太理想,但我可以手动管理这个约束。
答案 0 :(得分:8)
与其他数据库相比,SQL Server对可能的循环引用或多个删除路径非常保守。
你的来自NoteComment的多个删除路径:
Delete User -> Note -> NoteComment
Delete User -> NoteComment
一种解决方案是删除用户删除级联 - &gt; NoteComment并手动进行清理。
您还可以编写数据库触发器来进行清理。这是一个示例触发器:
CREATE TRIGGER [dbo].[Users_Delete_Cleanup]
ON [dbo].[Users]
INSTEAD OF DELETE
AS
BEGIN
IF @@ROWCOUNT = 0
RETURN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Delete NoteComment <--> User associations
DELETE nc FROM [dbo].[NoteComment] nc
JOIN DELETED dUser ON dUser.[Id] = nc.[User_Id]
-- Finally, delete user
DELETE u
FROM DELETED dUser
JOIN [dbo].[Users] u ON u.[Id] = dUser.[Id]
END
编辑 - 其他信息:
如果你还没有,我强烈推荐EF Power Tools extension。这使您能够右键单击任何实现DbContext的类并获取Entity Framework上下文菜单 - 在解决方案资源管理器中右键单击您的DbContext类 - &gt;实体框架 - &gt;查看DDL SQL
这将为您提供用于生成整个数据模型的Sql语句 - 确实非常有用,看看EF认为它正在构建什么。您可以尝试在SqlServer中手动运行它,并使其更接近它正在运行的错误。当EF正在构建DDL Sql时,如果没有编译错误,它通常会给你一些东西(或者一个完全神秘的空引用错误 - 然后检查你的输出窗口),但是有些东西可能无法在SqlServer中运行。
此外,您可以手动删除删除时的级联,以获得与fluent配置的一对多关系,除非您需要该属性,否则无需指定键:
modelBuilder.Entity<NoteComment>()
.HasRequired<User>(c => c.User)
.WithMany()
.WillCascadeOnDelete(false);
答案 1 :(得分:0)
您在User-&gt; Note Comments-&gt; Note-&gt; User-&gt;等之间有一个循环引用。
您拥有按键设置,您将永远不会以上述方式停止参考。
有许多不同的方法可以结束循环引用。例如,导致循环引用的属性上方的[ScriptIgnore]
属性,或者您可以执行树搜索方法,其中每个分支检查以确保所添加的对象不是父节点(递归地)一直到树的根节点。