在EF6.0 中,多对多关系配置时,系统会自动生成第三张表,来将两张有互相约束关系的表联系起来,但是在EF Core2.0中,我们需要手动建立第三张表,比如说有两个模型Passage.cs和Category.cs,若想建立两者之间的多对多关系,我们就需要借助第三张表PassageCategory来实现:
public class Passage{ //文章编号 [Key] public long PassageId { get; set; } //标题 public string Title { get; set; } //描述 public string Description { get; set; } //内容 public string Content { get; set; } //发布时间 public DateTime PublishTime { get; set; } //最后编辑时间 public DateTime LastEditTime { get; set; } //文章分类(使用技术等) public virtual IListPassageCategories { get; set; }}
public class Category{ [Key] public int CategoryId { get; set; } [MaxLength(50)] public string CategoryName { get; set; } public virtual IListPassageCategories { get; set; }}
public class PassageCategory{ public int CategoryId { get; set; } public Category Category { get; set; } public long PassageId { get; set; } public Passage Passage { get; set; }}
然后添加 FluentAPI 配置,在配置多对多关系时,必须指定级联删除。
先说一下EFCore的几种级联模式:
Cascade
依赖的实体也一并被删除。这种级联行为只对被上下文跟踪到的实体有效。数据库里也需要设置相应的级联,确保没有被上下文跟踪到的数据也具备同样的行为。如果你通过EF来创建数据库,那么EF会为你设置好数据库的级联。
Restrict
删除操作不会作用在依赖实体上,依赖实体保持不变。
SetNull
依赖实体的外键被设为null。这种级联行为只对被上下文跟踪到的实体有效。数据库里也需要设置相应的级联,确保没有被上下文跟踪到的数据也具备同样的行为。如果你通过EF来创建数据库,那么EF会为你设置好数据库的级联。
ClientSetNull
EFCore2.0引入了一种叫作ClientSetNull的默认行为。它具有SetNull的语义,兼有Restrict的行为。从我们的经验来看,对于被跟踪的实体和数据库来说,它是最被期待也是最有用的一种行为。
在为被跟踪的实体设置级联关系时,DeleteBehavior.Restrict已经成为历史。
添加一个新类 PassageCategoryMap.cs
该类继承自 IEntityTypeConfiguration
接口
public class PassageCategoryMap : IEntityTypeConfiguration{ /// /// PassageCategories FluentAPI配置 /// /// 添加复合主键、配置多对多关系 /// /// public void Configure(EntityTypeBuilderbuilder) { //添加复合主键 builder.HasKey(t => new { t.PassageId, t.CategoryId }); /// /// /// 配置Passage与PassageCategories的一对多关系 /// /// EFCore中,新增默认级联模式为ClientSetNull /// /// 依赖实体的外键会被设置为空,同时删除操作不会作用到依赖的实体上,依赖实体保持不变,同下 /// /// //配置Passage与PassageCategories的一对多关系 builder.HasOne(t => t.Passage).WithMany(p => p.PassageCategories).HasForeignKey(t => t.PassageId).OnDelete(DeleteBehavior.SetNull); //配置Category与PassageCategories的一对多关系 builder.HasOne(t => t.Category).WithMany(p => p.PassageCategories).HasForeignKey(t => t.CategoryId).OnDelete(DeleteBehavior.SetNull); } }
然后在 DbContext
类中,重写 OnModelCreating
方法添加 FluentAPI 配置
protected override void OnModelCreating(ModelBuilder builder){ base.OnModelCreating(builder); //查找所有FluentAPI配置 var typesToRegister = Assembly.GetExecutingAssembly().GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null); //应用FluentAPI foreach(var type in typesToRegister) { //dynamic使C#具有弱语言的特性,在编译时不对类型进行检查 dynamic configurationInstance = Activator.CreateInstance(type); builder.ApplyConfiguration(configurationInstance); }}
然后添加数据迁移,更新数据库,就完成了多对多关系数据库的配置。