Entity Framework сообщает циклическую ссылку, но это не имеет никакого смысла - PullRequest
2 голосов
/ 23 января 2012

Вот ок.код, с которым я работаю.

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

По какой-то причине, указав FK в классе NoteCommentкак свойство, допускающее обнуление, решило проблему.

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

Затем я удалил закомментированный код отображения в классе контекста данных.

Это не идеально, но я могу управлять этим ограничением вручную.

Ответы [ 2 ]

8 голосов
/ 23 января 2012

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 . Это дает вам возможность щелкнуть правой кнопкой мыши на любом классе, реализующем DbContext, и получить контекстное меню Entity Framework - Щелкните правой кнопкой мыши свой класс DbContext в Solution Explorer -> Entity Framework -> View DDL SQL

Это даст вам оператор Sql, используемый для генерации всей вашей модели данных - очень полезно, действительно, увидеть, что именно EF думает, что строит. Вы можете попробовать запустить его вручную в SqlServer и немного приблизиться к ошибкам, с которыми он сталкивается. Когда EF создает DDL Sql, если не считать ошибок компиляции, он обычно что-то вам даёт (или совершенно загадочную ошибку нулевой ссылки - затем проверьте окно вывода), но что-то может не работать в SqlServer.

Кроме того, вы можете вручную удалить каскад при удалении для одного-многих отношений с текущей конфигурацией, не нужно указывать ключи, если вы не хотите использовать это свойство:

modelBuilder.Entity<NoteComment>()
    .HasRequired<User>(c => c.User)
    .WithMany()
    .WillCascadeOnDelete(false);
0 голосов
/ 23 января 2012

У вас есть круговая ссылка между Пользователь-> Комментарии к заметкам-> Примечание-> Пользователь-> и т. Д.

Если у вас есть настройки ключей, на которые вы никогда не прекратите ссылаться вышеуказанным способом.

Есть много разных способов завершить циклическую ссылку. Например, атрибут [ScriptIgnore] над свойствами, которые вызывают циклическую ссылку, или вы можете выполнить метод поиска по дереву, где каждая ветвь проверяет, чтобы убедиться, что добавляемый объект не является родительским узлом (рекурсивно) на всем пути к корневой узел дерева.

...