Entity Framework: как решить "Ограничение FOREIGN KEY может вызвать циклы или несколько каскадных путей"?

есть много вопросов об этой проблеме, но я не мог решить свое дело. может кто-нибудь взглянуть на это:

У меня есть таблица Office, которая имеет отношение "один-много" с таблицами Doctor и Secretary. Обе последние таблицы производятся из таблицы Employee, которая имеет отношение общего первичного ключа с предопределенной таблицей Users, созданной sqlmembershipprovider. Кажется, существует много-много отношений между таблицей Users и таблицей Roles, в которой у меня нет никакой руки.

Моя проблема заключалась в создании отношения (zero, one) - (one) между моей таблицей Employee и таблицей Users, которую я закончил с общей связью первичных ключей между ними и возникшей ошибкой. (Есть ли лучшее решение для этой проблемы?)

Вот ошибка:

Представление ограничения FOREIGN KEY 'FK_dbo.aspnet_UsersInRoles_dbo.aspnet_Users_UserId' в таблице 'aspnet_UsersInRoles' может вызывать циклы или несколько каскадных путей. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION или измените другие Ограничения FOREIGN KEY. Не удалось создать ограничение. См. Предыдущие ошибки.

Вот мои коды и коды членства после обратной инженерии:

public class Office
{
    public Office()
    {
        this.Doctors = new HashSet<Doctor>();
        this.Secretaries = new HashSet<Secretary>();
    }

    [Key]
    public System.Guid OfficeId { get; set; }
    public virtual ICollection<Doctor> Doctors { get; set; }
    public virtual ICollection<Secretary> Secretaries { get; set; }
}

public class Employee
{
    [Key, ForeignKey("User")]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public System.Guid Id { get; set; }
    public string Name { get; set; }

    [ForeignKey("Office")]
    public System.Guid OfficeId { get; set; }

    // shared primary key 
    public virtual aspnet_Users User { get; set; }

    public virtual Office Office { get; set; }
}

public class Doctor :Employee
{
    public Doctor()
    {
        this.Expertises = new HashSet<Expertise>();
    }
    //the rest..    
    public virtual ICollection<Expertise> Expertises { get; set; }
}

public class Secretary : Employee
{
    // blah blah
}

public class aspnet_Users
{
    public aspnet_Users()
    {
        this.aspnet_Roles = new List<aspnet_Roles>();
    }

    public System.Guid ApplicationId { get; set; }
    public System.Guid UserId { get; set; }
    //the rest..
    public virtual aspnet_Applications aspnet_Applications { get; set; }
    public virtual ICollection<aspnet_Roles> aspnet_Roles { get; set; }
}

public class aspnet_Roles
{
    public aspnet_Roles()
    {
        this.aspnet_Users = new List<aspnet_Users>();
    }

    public System.Guid ApplicationId { get; set; }
    public System.Guid RoleId { get; set; }
    //the rest..
    public virtual aspnet_Applications aspnet_Applications { get; set; }
    public virtual ICollection<aspnet_Users> aspnet_Users { get; set; }
}

EDIT:, и отношения идут глубже, между таблицей Users и таблицей Applications существует много-одна связь, также между Roles и Applications.

Ответ 1

Вы можете свободно использовать api для указания действий, которые предлагает сообщение об ошибке.

В вашем контексте:

protected override void OnModelCreating( DbModelBuilder modelBuilder )
{
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<aspnet_UsersInRoles>().HasMany(i => i.Users).WithRequired().WillCascadeOnDelete(false);
}

Обратите внимание, что вы не включили определение таблицы aspnet_UsersInRoles, чтобы этот код не работал.

Другой вариант - удалить все CASCADE DELETES, добавив этот

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

Если вам нужна дополнительная информация о настройке отношений с текущим api, я предлагаю http://msdn.microsoft.com/en-US/data/jj591620

Ответ 2

Также вы можете изменить свой класс миграции. В моем случае в классе миграции был:

CreateTable(
    "dbo.Spendings",
    c => new
        {
          SpendingId = c.Int(nullable: false, identity: true),
          CategoryGroupId = c.Int(nullable: false),
          CategoryId = c.Int(nullable: false),
          Sum = c.Single(nullable: false),
          Date = c.DateTime(nullable: false),
          Comment = c.String(),
          UserId = c.String(),
          LastUpdate = c.String(),
        })
    .PrimaryKey(t => t.SpendingId)
    .ForeignKey("dbo.Categories", t => t.CategoryId, cascadeDelete: true)
    .ForeignKey("dbo.CategoryGroups", t => t.CategoryGroupId, cascadeDelete: true)
    .Index(t => t.CategoryGroupId)
    .Index(t => t.CategoryId);

После удаления "cascadeDelete: true" Update-Database работает отлично.

ИЛИ как false

 .ForeignKey("dbo.Categories", t => t.CategoryId, cascadeDelete: false)

Ответ 3

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
    }

Добавьте этот код в контекст

Ответ 4

Исходя из ваших комментариев, проблема в том, что вы используете один и тот же ПК в обеих таблицах. Я бы сразу изменил это, у нас есть ПК в качестве дополнительного поля в Employees с отношением FK к пользователям (один к одному, я думаю). И добавьте независимый ПК к пользователям (другой ориентир, чтобы уважать остальные ваши таблицы). Если вы хотите гарантировать, что новый внешний ключ в Employees (тот, который указывает на пользователей) уникален, вы всегда можете добавить уникальный индекс.

Таким образом, вы сохраняете свою структуру все равно, и вы избавляетесь от цикла.

Ответ 5

Просто Обновить:

Как и другие ответы, вам не нужно делать никаких действий при удалении. Обновленный способ сделать это для Entityframework Core 1.0

table.ForeignKey(name: "FK_Cities_Regions_RegionId",
                 column: x => x.RegionId,
                 principalTable: "Regions",
                 principalColumn: "RegionId",
                 onDelete: ReferentialAction.NoAction);

ПРИМЕЧАНИЕ. Сделайте ReferentialAction.NoAction на onDelete