EFCore中如何移除主外键关系

简介: 目录EFCore中如何移除主外键关系场景介绍主外键关系的问题解决思路禁止级联关系的生成MicroFX.EntityFrameworkCore.RemoveForeignKey扩展EFCore中如何移除主外键关系场景介绍我用EFCore写了一个blog程序,我要通过写文章来分享自己的知识,我定义了一个Article用来存放文章信息,我还定义了一个Category用来存放文章的分类,Category与Article是一对的关系。

目录

EFCore中如何移除主外键关系

场景介绍

我用EFCore写了一个blog程序,我要通过写文章来分享自己的知识,我定义了一个Article用来存放文章信息,我还定义了一个Category用来存放文章的分类,CategoryArticle是一对的关系。我的代码实现如下:

Article

public class Article
{
    public int Id {get;set;}
    
    public int CategoryId {get;set;}
    
    //导航属性,efcore会自动创建主外键关系
    public Category Category {get;set;}
}

Category

public class Category
{
    public int Id {get;set;}
    
    //导航属性,efcore会自动创建主外键关系
    public List<Article> Articles { get; set; }
}

MyBlogDbContext

public class MyBlogDbContext:DbContext
{
    public MyBlogDbContext(DbContextOptions options):base(options)
    {}
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        //Article
        var articleBuilder = modelBuilder.Entity<Article>();
        articleBuilder.ToTable("Article");
        articleBuilder.HasKey(article => article.Id);
        articleBuilder.HasIndex(article => article.CategoryId);
        
        //Category
        var categoryBuilder=modelBuilder.Entity<Category>();
        categoryBuilder.ToTable("Category");
        categoryBuilder.HasKey(category => category.Id);
    }
}

主外键关系的问题

  1. 当我想添加一片文章的时候,主外键要求我先添加这个文章的分类才允许我添加文章
  2. 当我想删除一个分类的时候,主要建会将我的文章也删除
  3. 总之,级联给我带来了很多烦恼

解决办法

  1. 修改数据,禁用级联功能
  2. 删除我们代码中的导航属性,阻止生成级联关系

以上两种办法都不是我想要的:

​ 我不想去操作数据库,因为我用了code first,ef会去操作数据库,所以我不想去修改数据库的级联功能(实际项目中我还是回去禁用数据库的级联关系)

​ 我也不想去删除导航属性,因为我想用ef core的Include功能。

解决思路

​ 在不修改数据的设置,也不删除导航属性的前提下实现禁用级联功能,我的做法是禁止级联关系的生成,可能你会说你这等于变相修改了sql,但是我确实没有写sql删除级联关系,也没有删除导航属性,总之,我的目的达到了,效果还不错。那么我是这么实现的?

禁止级联关系的生成

​ 我要做的是取翻看EFCore的代码,找到真正生成级联sql的地方然后重写,幸运的是我找到了,这个类就是SqlServerMigrationsSqlGenerator,我的实现如下:

CustomMigrationsSqlGeneratore

    public class CustomMigrationsSqlGeneratore : SqlServerMigrationsSqlGenerator
    {
        public CustomMigrationsSqlGeneratore( MigrationsSqlGeneratorDependencies dependencies,  IMigrationsAnnotationProvider migrationsAnnotations) : base(dependencies, migrationsAnnotations)
        {
        }
        //重写这个方法
        protected override void Generate(CreateTableOperation operation, IModel model, MigrationCommandListBuilder builder)
        {
            //删除级联关系
            RemoveForeignKeysHelper.ExecuForeignKeys(operation);
            base.Generate(operation, model, builder);
        }
    }

RemoveForeignKeysHelper

    public class RemoveForeignKeysHelper
    {
        //定义个全局变量,用来存储需要移除的级联属性
        internal static ConcurrentDictionary<string, List<string>> RemoveForeignKeys = new ConcurrentDictionary<string, List<string>>();

        public  static void ExecuForeignKeys(CreateTableOperation operation)
        {
            if (RemoveForeignKeys.TryGetValue(operation.Name, out List<string> columns))
            {
                operation.ForeignKeys
                    .Where(item => item.Columns.Intersect(columns).Count() > 0)
                    .ToList()
                    .ForEach(item => operation.ForeignKeys.Remove(item));
            }
        }
    }

EntityTypeBuilderExtensions

为EntityTypeBuilder添加扩展方法,通过扩展方法纪录那些需要被移除的级联关系

    public static class EntityTypeBuilderExtensions
    {
        public static EntityTypeBuilder<T> RemoeForeignKey<T>(this EntityTypeBuilder<T> builder,string name) where T : class
        {
            var tableName = builder.Metadata.FindAnnotation("Relational:TableName").Value.ToString();
            RemoveForeignKeysHelper.RemoveForeignKeys.AddOrUpdate(tableName, new List<string> { name },(value,values)=> {
                values.Add(name);
                return values.Distinct().ToList();
            });
            return builder;
        }
    }

DbContextOptionsBuilderExtensions

通过依赖注入,将生产sql的服务替换成我们自己的

    public static class DbContextOptionsBuilderExtensions
    {
        public static DbContextOptionsBuilder UseRemoveForeignKeyService(this DbContextOptionsBuilder options)
        {
            options.ReplaceService<IMigrationsSqlGenerator, CustomMigrationsSqlGeneratore>();
            return options;
        }
    }

到此,所有的代码都已经搞定,我们来看看怎么在我们的代码中引入这些功能。

首先,我们在创建Model的时候设置我要移除的级联关系,修改我们之前定义的MyBlogDbContext

MyBlogDbContext

public class MyBlogDbContext:DbContext
{
    public MyBlogDbContext(DbContextOptions options):base(options)
    {}
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        //Article
        var articleBuilder = modelBuilder.Entity<Article>();
        articleBuilder.ToTable("Article");
        articleBuilder.HasKey(article => article.Id);
        articleBuilder.HasIndex(article => article.CategoryId);
        
        ///你没有看错就是这么顺滑
        articleBuilder.RemoeForeignKey("CategoryId");
        
        //Category
        var categoryBuilder=modelBuilder.Entity<Category>();
        categoryBuilder.ToTable("Category");
        categoryBuilder.HasKey(category => category.Id);
    }
}

然后,将IMigrationsSqlGenerator替换成我们自定义的类CustomMigrationsSqlGeneratore

//AddDbContxt记得吧,在Startup中或者在你自己扩展的IServiceCollection方法中
service.AddDbContext<MicroBlogDbContext>(options =>
            {
                //核心操作就在这里
                options.UseRemoveForeignKeyService();
                options.UseMySql(connStr,config=> {
                    config.CharSetBehavior(CharSetBehavior.AppendToAllColumns);
                    config.AnsiCharSet(CharSet.Latin1);
                    config.UnicodeCharSet(CharSet.Utf8mb4);
                });
            });

完!

MicroFX.EntityFrameworkCore.RemoveForeignKey扩展

​ 我写了个扩展,目前支持mysql和sqlserver,如果有机会我也会实现其它数据库的扩展。

MicroFX.EntityFrameworkCore.RemoveForeignKey

Micro.EntityFrameworkCore.RemoveForeignKey,SqlServer

Micro.EntityFrameworkCore.RemoveForeignKey.MySql

目录
相关文章
解决开启子线程,导致request上下文和session信息丢失问题
解决开启子线程,导致request上下文和session信息丢失问题
1500 0
|
NoSQL Java 数据库
MongoDB 分组统计
   MongoDB 作为 NoSql 文档型数据库,在全球范围得到广泛的支持与应用。在比较常用的数据库功能中,相对于普通的增删改查,使用 group 聚合分组统计有些复杂,而 MongoDB 也给予了支持。
3742 0
|
Kubernetes 架构师 Java
史上最全对照表:大厂P6/P7/P8 职业技能 薪资水平 成长路线
40岁老架构师尼恩,专注于帮助读者提升技术能力和职业发展。其读者群中,多位成员成功获得知名互联网企业的面试机会。尼恩不仅提供系统化的面试准备指导,还特别针对谈薪酬环节给予专业建议,助力求职者在与HR谈判时更加自信。此外,尼恩还分享了阿里巴巴的职级体系,作为行业内广泛认可的标准,帮助读者更好地理解各职级的要求和发展路径。通过尼恩的技术圣经系列PDF,如《尼恩Java面试宝典》等,读者可以进一步提升自身技术实力,应对职场挑战。关注“技术自由圈”公众号,获取更多资源。
|
数据采集 Cloud Native 关系型数据库
实现业务零停机!NineData的PostgreSQL数据迁移能力解析
NineData推出了PostgreSQL业务不停服数据迁移能力。NineData实现了完全自动化的结构迁移和全量数据迁移,并提供了变更数据的迁移能力。这种能力可以实时监听源PostgreSQL中的变更数据,在完成全量迁移后将变更数据实时复制到目标PostgreSQL,实现源/目标PostgreSQL的动态复制。在PostgreSQL数据迁移过程中,业务可以正常提供服务,无需停服。最终,业务可以根据需求选择对应的时间点切换到目标PostgreSQL。
862 1
|
存储 人工智能 大数据
拼多多详情API的价值与应用解析
拼多多作为中国电商市场的重要参与者,其开放平台提供的商品详情API接口为电商行业带来了新的机遇和挑战。该接口允许开发者通过编程方式获取商品的详细信息,包括标题、价格、描述、图片、规格参数和库存等,推动了电商运营的智能化和高效化。本文将深入解析拼多多详情API的价值与应用,帮助商家和开发者更好地理解和利用这一宝贵资源。
600 0
|
缓存 安全 数据安全/隐私保护
【Docker专栏】深入理解Docker镜像的构建与推送
【5月更文挑战第7天】本文介绍了Docker镜像的核心作用及基础概念,包括镜像作为容器模板的特性。文章详细阐述了Dockerfile的编写,例如设置基础镜像、工作目录、安装依赖及定义启动命令。通过`docker build`命令构建镜像,并提示了优化构建过程的技巧。此外,还讲解了如何将镜像推送到远程仓库,包括选择仓库、认证、标签和推送镜像的步骤,以及镜像安全性的考虑,如扫描漏洞和遵循最小权限原则。本文旨在帮助读者掌握Docker镜像的构建与推送,以高效管理容器化应用。
677 61
【Docker专栏】深入理解Docker镜像的构建与推送
|
SQL Oracle 关系型数据库
关系型数据库Oracle 数据库启动失败
【7月更文挑战第17天】
495 2
|
数据库
优化数据加载策略:深入探讨Entity Framework Core中的懒加载与显式加载技术及其适用场景
【8月更文挑战第31天】在 Entity Framework Core(EF Core)中,数据加载策略直接影响应用性能。本文将介绍懒加载(Lazy Loading)和显式加载(Eager Loading)的概念及适用场景。懒加载在访问导航属性时才加载关联实体,可优化性能,但可能引发多次数据库查询;显式加载则一次性加载所有关联实体,减少查询次数但增加单次查询的数据量。了解这些策略有助于开发高性能应用。
257 0
|
SQL 关系型数据库 MySQL
MySQL外键约束行为解析:CASCADE, NO ACTION, RESTRICT, SET NULL
MySQL外键约束行为解析:CASCADE, NO ACTION, RESTRICT, SET NULL
2092 0
|
SQL 开发框架 缓存
【Entity Framework】 EF中DbContext类详解
【Entity Framework】 EF中DbContext类详解
507 0