实体框架报告循环引用,但它没有任何意义

时间:2012-01-23 19:42:08

标签: entity-framework ef-code-first sql-server-ce code-first circular-reference

这是约。我正在使用的代码。

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; }
}

然后我删除了数据上下文类中注释掉的映射代码。

这不太理想,但我可以手动管理这个约束。

2 个答案:

答案 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]属性,或者您可以执行树搜索方法,其中每个分支检查以确保所添加的对象不是父节点(递归地)一直到树的根节点。