【问题标题】:Entity Framework reports circular reference, but it doesn't make any senseEntity Framework 报告循环引用,但没有任何意义
【发布时间】:2012-01-23 19:42:07
【问题描述】:

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

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 => User 的导航属性在Note => NoteComment 之间生成循环引用。


EDIT

由于某种原因,将 NoteComment 类中的 FK 指定为可为空的属性解决了该问题。

public class NoteComment {
  public Guid? UserId { get; set; }
  [ForeignKey("UserId")]
  public virtual User User { get; set; }
}

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

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

【问题讨论】:

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


    【解决方案1】:

    与其他数据库相比,SQL Server 对于可能的循环引用或多个删除路径非常保守。

    您的来自 NoteComment 的多个删除路径:

    Delete User -> Note -> NoteComment
    Delete User -> NoteComment
    

    一种解决方案是删除 Cascade On Delete for User -> 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 的类并获取实体框架上下文菜单 - 在解决方案资源管理器中右键单击您的 DbContext 类 -> 实体框架 -> 查看 DDL SQL

    这将为您提供用于生成整个数据模型的 Sql 语句 - 对于查看 EF 认为它正在构建的确切内容非常有用。您可以尝试在 SqlServer 中手动运行它,并更接近它所遇到的错误。当 EF 构建 DDL Sql 时,如果没有编译错误,它通常会给你一些东西(或者一个完全神秘的空引用错误 - 然后检查你的输出窗口),但这些东西可能不会在 SqlServer 中运行。

    此外,您可以手动删除级联删除与流畅配置的一对多关系,除非您想要该属性,否则无需指定键:

    modelBuilder.Entity<NoteComment>()
        .HasRequired<User>(c => c.User)
        .WithMany()
        .WillCascadeOnDelete(false);
    

    【讨论】:

    • 好的,但仍然对此感到困惑的是,如果我注释掉映射代码,EF 仍然会从 NoteComments => User 生成自己的 FK,但它使用属性名称 User_ID,这就是我正在尝试使用手动映射进行更改。如果 SQL Server 在级联删除方面存在问题,为什么它会允许 EF 生成这种关系?
    • 这是一个 EF CodeFirst 约定 - 在模型生成过程中根本不涉及 SqlServer。 EF 会自行生成架构,而不管 SqlServer 是否接受它。当您手动将键指定为可为空的类型时,将删除删除时的级联。
    【解决方案2】:

    您在 User->Note Comments->Note->User->etc..之间有一个循环引用。

    如果您有 Keys 设置,您将永远不会停止以上述方式引用。

    有许多不同的方法可以结束循环引用。例如,[ScriptIgnore] 属性在导致循环引用的属性上方,或者您可以执行树搜索方法,其中每个分支检查以确保添加的对象不是父节点(递归地)一直到树的根节点。

    【讨论】:

      猜你喜欢
      • 2019-11-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-31
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      相关资源
      最近更新 更多