Указание ON DELETE NO ACTION в Entity Framework 7?

В Entity Framework 7, когда я пытаюсь применить миграцию, я получаю сообщение об ошибке

Введение ограничения FOREIGN KEY «FK_ChangeOrder_User_CreatedByID» в таблице «ChangeOrder» может вызвать циклы или множественные каскадные пути. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION или измените другие ограничения FOREIGN KEY.
Не удалось создать ограничение. См. предыдущие ошибки.

Я знаю, что в более старых версиях Entity Framework вы бы справились с этим, добавив

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

в DbContext, но в EF7 modelBuilder, похоже, нет .Conventions, и Google возвращает только старые результаты EF 4, хотя результаты EF 6.

Как указать ограничение ON DELETE NO ACTION в Entity Framework 7?

Изменить: ответ, предоставленный Олегом, по-видимому, сделает это для каждого внешнего ключа, но я хотел бы сделать это глобально, так как будет намного проще использовать одну строку кода, чтобы объявить это глобально, а затем указать код для каждого из них. сотни отношений, которые у меня будут в конечном итоге.

Редактировать 2: Код для Олега

public class ChangeOrder
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }

    public Int16? ApprovedByID { get; set; }
    public Byte ApprovalStatusID { get; set; }
    public Int16 AssignedToID { get; set; }
    public Int16 CreatedByID { get; set; }
    public Byte CurrentStatusID { get; set; }
    public DateTime? DateApproved { get; set; }
    public DateTime? EndDate { get; set; }
    public Byte ImpactID { get; set; }
    public Byte PriorityID { get; set; }
    public DateTime? StartDate { get; set; }
    public Byte TypeID { get; set; }

    [Required]
    public string Name { get; set; }

    [Required]
    public string ReasonForChange { get; set; }

    [ForeignKey("ApprovedByID")]
    public User ApprovedBy { get; set; }

    [ForeignKey("ApprovalStatusID")]
    public ChangeApprovalStatus ApprovalStatus { get; set; }

    [ForeignKey("AssignedToID")]
    public User AssignedTo { get; set; }

    [ForeignKey("CreatedByID")]
    public User CreatedBy { get; set; }

    [ForeignKey("ImpactID")]
    public ChangeImpact Impact { get; set; }

    [ForeignKey("PriorityID")]
    public ChangePriority Priority { get; set; }

    [ForeignKey("TypeID")]
    public ChangeType ChangeType { get; set; }

    [ForeignKey("CurrentStatusID")]
    public ChangeStatus CurrentStatus { get; set; }
}
public class JobSightDBContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelbuilder)
    {
        base.OnModelCreating(modelbuilder);
    }

    DbSet<ChangeApprovalStatus> ChangeApprovalStatus { get; set; }
    DbSet<ChangeImpact> ChangeImapct { get; set; }
    DbSet<ChangeOrder> ChangeOrders { get; set; }
    DbSet<ChangePriority> ChangePriorities { get; set; }
    DbSet<ChangeStatus> ChangeStatus { get; set; }
    DbSet<ChangeType> ChangeTypes { get; set; }
    DbSet<User> Users { get; set; }
}

person Matthew Verstraete    schedule 13.01.2016    source источник


Ответы (2)


После копания на GitHub и работы с очень терпеливым парнем из MS текущее решение состоит в том, чтобы добавить это в DbContext

protected override void OnModelCreating(ModelBuilder modelbuilder)
{
    foreach (var relationship in modelbuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
    {
        relationship.DeleteBehavior = DeleteBehavior.Restrict;
    }

    base.OnModelCreating(modelbuilder);
}
person Matthew Verstraete    schedule 13.01.2016
comment
это спасло мне жизнь!!!!! У меня было много проблем с моделированием FK. Бесконечно благодарен!!!!!!!!!!!!!! - person The_Butcher; 15.01.2018
comment
DeleteBehavior есть, а UpdateBehavior нет? - person Krzaku; 20.03.2018
comment
Это не работает в EF Core 2.1 :( - person Lewis Cianci; 11.04.2018
comment
У меня не работает: «DbModelBuilder» не содержит определения «Модель». - person Matteo Gariglio; 11.04.2018
comment
Это сработало для меня на EF Core 2.0.2. Просто убедитесь, что у вас есть base.OnModelCreating(modelbuilder) и указанный выше метод до ваших привязок. - person djangojazz; 19.04.2018
comment
У меня это работало на EF Core 2.1, но, как упоминалось ранее, base.OnModelCreating(modelbuilder); должен быть вверху. - person Rubanov; 03.07.2018
comment
Просто добавить заметку, если она не видит SelectMany (по умолчанию), добавить с помощью System.Linq; - person Steve McNiven-Scott; 05.09.2018
comment
Вы - гений . Это работает волшебным образом !! - person Zeinab; 26.04.2019
comment
отличные строки. Спасибо - person Gabriel Castillo Prada; 07.12.2020

Постройка

modelBuilder.Entity("myNamespace.Models.ChangeOrder", b =>
    {
        b.HasOne("myNamespace.Models.User")
            .WithMany()
            .HasForeignKey("CreatedByID")
            .OnDelete(DeleteBehavior.Cascade);
    });

будет означать создание FK_ChangeOrder_User_CreatedByID с REFERENCES [dbo].[User] ([CreatedByID]) ON DELETE CASCADE. Он должен существовать в protected override void BuildModel(ModelBuilder modelBuilder) из YourContextModelSnapshot.cs, созданных во время миграции. Я не уверен, что полностью понял ваш вопрос, но я думаю, что вам следует либо добавить такую ​​конструкцию в XXXModelSnapshot.cs, либо удалить ненужную конструкцию, которая уже есть здесь.

ОБНОВЛЕНО: я вижу, что у вас проблема в модели. У вас есть следующие свойства в

public Int16? ApprovedByID { get; set; }
public Int16 AssignedToID { get; set; }
public Int16 CreatedByID { get; set; }

// navigation properties

[ForeignKey("ApprovedByID")]
public User ApprovedBy { get; set; }

[ForeignKey("AssignedToID")]
public User AssignedTo { get; set; }

[ForeignKey("CreatedByID")]
public User CreatedBy { get; set; }

При миграции по умолчанию попробуйте установить DeleteBehavior.Cascade для всех свойств.

Вы можете перезаписать поведение, изменив OnModelCreating, который задает либо DeleteBehavior.Restrict поведение для всех клавиш, либо устанавливает для одной клавиши поведение DeleteBehavior.Cascade или DeleteBehavior.SetNull. Например, в приведенном ниже коде используется DeleteBehavior.Cascade для CreatedByID (что создает ON DELETE CASCADE для внешних ключей) и DeleteBehavior.Restrict для других внешних ключей (без ON DELETE для внешних ключей):

public class JobSightDBContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelbuilder)
    {
        base.OnModelCreating(modelbuilder);

        modelbuilder.Entity(typeof (ChangeOrder))
            .HasOne(typeof (User), "ApprovedBy")
            .WithMany()
            .HasForeignKey("ApprovedByID")
            .OnDelete(DeleteBehavior.Restrict); // no ON DELETE
        modelbuilder.Entity(typeof (ChangeOrder))
            .HasOne(typeof (User), "AssignedTo")
            .WithMany()
            .HasForeignKey("AssignedToID")
            .OnDelete(DeleteBehavior.Restrict); // no ON DELETE
        modelbuilder.Entity(typeof (ChangeOrder))
            .HasOne(typeof (User), "CreatedBy")
            .WithMany()
            .HasForeignKey("CreatedByID")
            .OnDelete(DeleteBehavior.Cascade); // set ON DELETE CASCADE
    }

    DbSet<ChangeApprovalStatus> ChangeApprovalStatus { get; set; }
    DbSet<ChangeImpact> ChangeImapct { get; set; }
    DbSet<ChangeOrder> ChangeOrders { get; set; }
    DbSet<ChangePriority> ChangePriorities { get; set; }
    DbSet<ChangeStatus> ChangeStatus { get; set; }
    DbSet<ChangeType> ChangeTypes { get; set; }
    DbSet<User> Users { get; set; }
}
person Oleg    schedule 13.01.2016
comment
Есть ли способ сделать это глобально или мне нужно сделать это для каждого из сотен отношений, которые у меня есть? Выполнение этого таким образом создаст для меня много дополнительного кода, и я бы предпочел этого не делать. - person Matthew Verstraete; 13.01.2016
comment
@MatthewVerstraete: трудно ответить, имея только сообщение об ошибке. Я предполагаю, что вы сначала создали набор классов, которые имеют много обозначений данных, а затем создали XXXContext, производные от DbContext, которые имеют много свойств, таких как public DbSet<User> Users { get; set; }, public DbSet<ChangeOrder> ChangeOrders { get; set; }. Я предполагаю, что некоторые определения в User, ChangeOrder будут неправильно интерпретированы EF Migration, но я не могу предположить, что именно это может быть. Нужно проанализировать полную модель, чтобы найти причину. - person Oleg; 13.01.2016
comment
Не уверен, что у вас проблемы с пониманием. Я ищу версию EF7 для modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();, но я добавил DBContext и модель, на которую вы можете посмотреть - person Matthew Verstraete; 13.01.2016
comment
@MatthewVerstraete: Я хочу вкратце сообщить вам, что сейчас мне нужно сделать несколько важных вещей для моего клиента. Я вернусь к вашей проблеме позже (через несколько часов). - person Oleg; 13.01.2016
comment
@MatthewVerstraete: я обновил свой ответ. См. OnModelCreating в разделе ОБНОВЛЕНО. - person Oleg; 13.01.2016
comment
Да, это будет сделано для каждого свойства, но, как уже говорилось, я хочу сделать это глобально. В конечном итоге у меня будут сотни свойств, к которым это нужно применить, и я не хочу делать ни одного из них по отдельности, оставляя много места для ошибок. - person Matthew Verstraete; 13.01.2016
comment
@MatthewVerstraete: я не понимаю, что ты хочешь сказать. Вы можете видеть, что EF7 пытается установить по умолчанию ON DELETE CASCADE. Таким образом, вы должны сообщить EF7, на каком из таких полей вам действительно нужно установить ON DELETE CASCADE, а на каком NO ACTION. Я показал вам, как предоставить EF7 необходимую информацию. Вы должны сами решить, как реализовать это в своей среде. - person Oleg; 13.01.2016
comment
@MatthewVerstraete: Например, вы можете расширить ChangeOrder с помощью `public static void SetOnDelete(ModelBuilder modelbuilder) {...}`, который вносит изменения из OnModelCreating и вызывает ChangeOrder.SetOnDelete(modelbuilder); в OnModelCreating. Таким образом, вы можете легко расширить JobSightDBContext. - person Oleg; 13.01.2016
comment
@Olga Да, поведение EF7 по умолчанию — установить ON DELETE CASCADE. Я хочу переопределить это и удалить это поведение, чтобы значение по умолчанию стало ON DELTE NO ACTION. В EF6 это было сделано с помощью modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();. То, что вы показали мне, - это то, как делать это по одному для каждого свойства. Мне нужно знать, как установить DeleteBehavior.Restrict в качестве поведения по умолчанию для EF7. - person Matthew Verstraete; 13.01.2016