简体   繁体   English

ASP.NET MVC级联删除

[英]ASP.NET MVC cascade delete

I have two models. 我有两个模型。 Town: 镇:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace CpuRegistry.Models
{    
  public enum TownType
  {
      Город, Посёлок, Село
  }

  public class Town
  {
      public int ID { get; set; }

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

      [Required]
      public int RegionID { get; set; }

      public int? DistrictID { get; set; }

      [Required]
      public TownType TownType { get; set; }

      public virtual ICollection<District> Districts { get; set; }        
      public virtual ICollection<Primary> Primaries { get; set; }
  }
}

and District: 和区:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;

namespace CpuRegistry.Models
{
    public class District
    {
        public int ID { get; set; }

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

        [Required]
        public int RegionID { get; set; }

        public int? TownID { get; set; }

        public virtual ICollection<Town> Towns { get; set; }
        public virtual ICollection<Primary> Primaries { get; set; }
    }
}

Town can belong to District. 镇可以属于区。 Disctrict can belong to Town. 区可以属于镇。 Also District can have few towns, Town can have few districts. 另外,区可以有几个镇,镇可以有几个区。 As you can see, I marked DistrictID and TownID with "?". 如您所见,我用“?”标记了DistrictID和TownID。

When I use command Add-Migration, Visual Studio create migration file, which contains: 当我使用命令“添加迁移”时,Visual Studio将创建迁移文件,其中包含:

CreateTable(
                "dbo.TownDistricts",
                c => new
                    {
                        Town_ID = c.Int(nullable: false),
                        District_ID = c.Int(nullable: false),
                    })
                .PrimaryKey(t => new { t.Town_ID, t.District_ID })
                .ForeignKey("dbo.Towns", t => t.Town_ID, cascadeDelete: true)
                .ForeignKey("dbo.Districts", t => t.District_ID, cascadeDelete: true)
                .Index(t => t.Town_ID)
                .Index(t => t.District_ID);

After using command Update-Database, I have next error: 使用命令更新数据库后,出现下一个错误:

Introducing FOREIGN KEY constraint 'FK_dbo.TownDistricts_dbo.Districts_District_ID' on table 'TownDistricts' may cause cycles or multiple cascade paths. 在表“ TownDistricts”上引入FOREIGN KEY约束“ FK_dbo.TownDistricts_dbo.Districts_District_ID”可能会导致循环或多个级联路径。 Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。

I saw this link: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why? 我看到了此链接: 引入FOREIGN KEY约束可能会导致循环或多个级联路径-为什么? Quote from solution: 引用解决方案:

You must either make the Stage optional in at least one of the entities (ie remove the [Required] attribute from the Stage properties) 您必须使舞台在至少一个实体中成为可选(即从舞台属性中删除[Required]属性)

But I don't have [Required] attribute. 但是我没有[Required]属性。 Also I tried this solution: 我也尝试了这种解决方案:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Town>().HasRequired(t => t.Districts).WithMany().WillCascadeOnDelete(false);
            modelBuilder.Entity<District>().HasRequired(d => d.Towns).WithMany().WillCascadeOnDelete(false);
        }

And have error: 并有错误:

CpuRegistry.DataContexts.IdentityUserRole: : EntityType 'IdentityUserRole' has no key defined. CpuRegistry.DataContexts.IdentityUserRole::EntityType'IdentityUserRole'没有定义键。 Define the key for this EntityType. 定义此EntityType的键。 CpuRegistry.DataContexts.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no key defined. CpuRegistry.DataContexts.IdentityUserLogin::EntityType'IdentityUserLogin'没有定义键。 Define the key for this EntityType. 定义此EntityType的键。 IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based on type 'IdentityUserRole' that has no keys defined. IdentityUserRoles:EntityType:EntitySet'IdentityUserRoles'基于未定义键的'IdentityUserRole'类型。 IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is based on type 'IdentityUserLogin' that has no keys defined. IdentityUserLogins:EntityType:EntitySet'IdentityUserLogins'基于未定义键的'IdentityUserLogin'类型。

And when I open migration file, which creates user tables, I saw something similar to my code: 当我打开创建用户表的迁移文件时,我看到了类似于我的代码的内容:

CreateTable(
                "dbo.AspNetUserRoles",
                c => new
                    {
                        UserId = c.String(nullable: false, maxLength: 128),
                        RoleId = c.String(nullable: false, maxLength: 128),
                    })
                .PrimaryKey(t => new { t.UserId, t.RoleId })
                .ForeignKey("dbo.AspNetRoles", t => t.RoleId, cascadeDelete: true)
                .ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true)
                .Index(t => t.UserId)
                .Index(t => t.RoleId);

But I cannot find, how developers solved this in models or something else. 但是我找不到开发人员如何在模型或其他方面解决这个问题。

Thanks for help! 感谢帮助!

According to Configure Many-to-Many relationship using Code First Approach you do not need explicit foreign key declaration public int? TownID { get; set; } 根据使用代码优先方法配置多对多关系,您不需要显式外键声明public int? TownID { get; set; } public int? TownID { get; set; } public int? TownID { get; set; } in class District and vice versa (in another class). public int? TownID { get; set; }在“ District ,反之亦然(在另一类中)。 It seems that Entity Framework is able to configure all necessary connections (additional table in database) just using declared navigation collections like public virtual ICollection<Town> Towns { get; set; } 似乎Entity Framework能够使用声明的导航集合(如public virtual ICollection<Town> Towns { get; set; }来配置所有必要的连接(数据库中的附加表) public virtual ICollection<Town> Towns { get; set; } public virtual ICollection<Town> Towns { get; set; } public virtual ICollection<Town> Towns { get; set; } . public virtual ICollection<Town> Towns { get; set; }

Addition . 加法 What do you mean when declare both single property public int? DistrictID { get; set; } 声明两个单一属性public int? DistrictID { get; set; }是什么意思public int? DistrictID { get; set; } public int? DistrictID { get; set; } public int? DistrictID { get; set; } and a collection public virtual ICollection<District> Districts { get; set; } public int? DistrictID { get; set; }和集合public virtual ICollection<District> Districts { get; set; } public virtual ICollection<District> Districts { get; set; } public virtual ICollection<District> Districts { get; set; } . public virtual ICollection<District> Districts { get; set; } Do you confuse the concepts of many-to-many relationship in EF or did you mean that these properties denote something different? 您是否混淆了EF中多对多关系的概念,还是意味着这些属性表示不同的东西? If they denote something different then you should use Fluent API to configure them properly. 如果它们表示不同的内容,则应使用Fluent API正确配置它们。

Update . 更新 After OP's clarifications I suggest the following code. 经过OP的澄清后,我建议使用以下代码。

public class TestEfContext : ApplicationDbContext {
    public DbSet<Town> Towns { get; set; }
    public DbSet<District> Districts { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Town>()
            .HasMany<District>(x => x.Districts)
            .WithOptional(x => x.Town);

        modelBuilder.Entity<District>()
            .HasMany<Town>(x => x.Towns)
            .WithOptional(x => x.District);
    }
}

public class Town {
    [Key]
    public int Id { get; set; }

    public District District { get; set; }

    public virtual ICollection<District> Districts { get; set; }

    public override string ToString() {
        return Id.ToString();
    }
}

public class District {
    [Key]
    public int Id { get; set; }

    public Town Town { get; set; }

    public virtual ICollection<Town> Towns { get; set; }

    public override string ToString() {
        return Id.ToString();
    }
}

Thus 从而

using (var ctx = new TestEfContext()) {
            ctx.Towns.First().District = ctx.Districts.First();
        }

entails that the Town1 will have District = Distr1 and empty Districts collection while the Distr1 will have Town = null and Towns collection with one element - Town1. 需要Town1具有District = Distr1和空的Districts集合,而Distr1将具有Town = null,并且Towns集合具有一个元素-Town1。

So, the final classes, which solves my first question and problems from messages above, are: 因此,解决上述问题的第一个问题的最终课程是:

namespace CpuRegistry.DataContexts
{
    public class CpuRegistryDb : IdentityDbContext<ApplicationUser>
    {
        public CpuRegistryDb()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }

        public static CpuRegistryDb Create()
        {
            return new CpuRegistryDb();
        }

        public DbSet<Region> Regions { get; set; }
        public DbSet<District> Districts { get; set; }
        public DbSet<Town> Towns { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Town>().HasMany<District>(t => t.Districts).WithOptional(d => d.Town);
            modelBuilder.Entity<District>().HasMany<Town>(d => d.Towns).WithOptional(t => t.District);            
        }
    }
}

namespace CpuRegistry.Models
{
    public enum TownType
    {
        Город, Посёлок, Село
    }

    public class Town
    {
        public int ID { get; set; }

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

        [Required]
        public int RegionID { get; set; }

        public int? DistrictID { get; set; }

        [Required]
        public TownType TownType { get; set; }

        public virtual ICollection<District> Districts { get; set; }
        public virtual Region Region { get; set; }
        public virtual District District { get; set; }

        public override string ToString()
        {
            return ID.ToString();
        }
    }

    public class District
    {
        public int ID { get; set; }

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

        [Required]
        public int RegionID { get; set; }

        public int? TownID { get; set; }

        public virtual ICollection<Town> Towns { get; set; }
        public virtual Region Region { get; set; }
        public virtual Town Town { get; set; }

        public override string ToString()
        {
            return ID.ToString();
        }
    }
}

Thanks @Hoborg for help. 感谢@Hoborg提供帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM