EFCore实现数据库水平分表的方法

EFCore实现数据库水平分表的方法MySQL 使用 EFCore 框架水平分表的实现方法 即一个实体映射到多个数据表 通过该方法可以实现动态映射 根据不同的条件映射到不同的数据表

水平分表

代码运行环境

经检验,EFCore 3.0以上已经不能使用此方法了,EFCore 3.0以上的实现代码请直接移步到下文中的 EFCore 3.x 节。

实现方法

这里Post类对应两个数据表:post_odd 和 post_even

代码

Program.cs

using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; namespace TableMappingTest { 
    class Program { 
    static void Main(string[] args) { 
    string table1 = "post_odd"; string table2 = "post_even"; BloggingContext context = new BloggingContext(); // step1:改变实体模型缓存工厂的返回值,使EFCore认为Model已经发生改变,下次使用实体前将更新模型映射 DynamicModelCacheKeyFactory.ChangeTableMapping(); // step2:获取实体模型Post的映射(这里使用了实体模型,所以会更新模型映射) if (context.Model.FindEntityType(typeof(Post))?.Relational() is RelationalEntityTypeAnnotations relational) { 
    // step3:修改Post实体映射的数据表 relational.TableName = table1; } // 此时该context内Post实体的映射表已经是 post_odd, 就算重复以上3步也不会改变,除非重新new一个 List<Post> list1 = context.Set<Post>().Where(s => true).ToList(); Console.WriteLine(table1); PrintList(list1); // 改另一个表测试 BloggingContext context_1 = new BloggingContext(); DynamicModelCacheKeyFactory.ChangeTableMapping(); if (context_1.Model.FindEntityType(typeof(Post))?.Relational() is RelationalEntityTypeAnnotations r) { 
    r.TableName = table2; } List<Post> list2 = context_1.Set<Post>().Where(s => true).ToList(); Console.WriteLine(table2); PrintList(list2); Console.ReadKey(); } static void PrintList(List<Post> list) { 
    foreach(Post item in list) { 
    Console.WriteLine(item); } Console.WriteLine(); } } } 

Models.cs

using System; using System.Collections.Generic; using System.ComponentModel; using System.Text; using System.Threading; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; namespace TableMappingTest { 
    ///  /// 用于替换的模型缓存工厂 ///  public class DynamicModelCacheKeyFactory : IModelCacheKeyFactory { 
    private static int m_Marker = 0; ///  /// 改变模型映射,只要Create返回的值跟上次缓存的值不一样,EFCore就认为模型已经更新,需要重新加载 ///  public static void ChangeTableMapping() { 
    Interlocked.Increment(ref m_Marker); } ///  /// 重写方法 ///  /// context模型 /// 
    public object Create(DbContext context) { 
    return (context.GetType(), m_Marker); } } // Context模型 public class BloggingContext : DbContext { 
    public virtual DbSet<Blog> Blogs { 
    get; set; } public virtual DbSet<Post> Posts { 
    get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { 
    // step0: 调用ReplaceService替换掉默认的模型缓存工厂 optionsBuilder.UseMySQL("连接字符串") .ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactory>() .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); } protected override void OnModelCreating(ModelBuilder modelBuilder) { 
    modelBuilder.Entity<Blog>(entity => { 
    entity.HasKey(e => e.BlogId); entity.ToTable("blog"); entity.HasIndex(s => s.UserId) .HasName("blog_user_FK_index"); entity.Property(e => e.BlogId) .HasColumnName("blogid") .HasColumnType("int(11)") .ValueGeneratedOnAdd(); entity.Property(e => e.Rating) .HasColumnName("rating") .HasColumnType("int(11)"); entity.Property(e => e.UserId) .HasColumnType("int(11)") .HasColumnName("userId"); }); modelBuilder.Entity<Post>(entity => { 
    entity.HasKey(e => e.PostId); entity.ToTable("post"); entity.HasIndex(e => e.BlogId) .HasName("post_blog_FK_idx"); entity.Property(e => e.PostId) .HasColumnName("postid") .HasColumnType("int(11)") .ValueGeneratedOnAdd(); entity.Property(e => e.Title) .HasColumnName("title") .HasMaxLength(64); entity.Property(e => e.Content) .HasColumnName("content") .HasMaxLength(1024); entity.Property(e => e.BlogId) .HasColumnName("blogId") .HasColumnType("int(11)"); entity.HasOne(e => e.Blog) .WithMany(s => s.Posts); }); } } public class Blog { 
    public Blog() { 
    Posts = new HashSet<Post>(); } public int BlogId { 
    get; set; } public string Url { 
    get; set; } public int Rating { 
    get; set; } public int UserId { 
    get; set; } [DefaultValue(null)] public ICollection<Post> Posts { 
    get; set; } // 为了方便测试就重写了ToString方法 public override string ToString() { 
    return $"Id: { 
     BlogId} Url: { 
     Url}"; } } public class Post { 
    public int PostId { 
    get; set; } public string Title { 
    get; set; } public string Content { 
    get; set; } public int BlogId { 
    get; set; } [DefaultValue(null)] public Blog Blog { 
    get; set; } // 为了方便测试就重写了ToString方法 public override string ToString() { 
    return $"Id: { 
     PostId} Title: { 
     Title}"; } } } 

实现步骤

EFCore 3.x

进一步封装

以上的例子虽然很简陋,但功能是实现了。倘若需要应用到项目里,这种封装程度是远远不够的,还需要对代码进行进一步封装。以下是我本人对以上代码的一个简单封装,希望能帮助到有需要的同学。

知识点总结

参考文章

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224519.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 上午11:45
下一篇 2026年3月17日 上午11:45


相关推荐

  • 在WAMPSERVER下增加多版本的PHP(PHP5.3,PHP5.4,PHP5.5)支持。

    在WAMPSERVER下增加多版本的PHP(PHP5.3,PHP5.4,PHP5.5)支持。

    2021年9月2日
    57
  • Coze插件开发实战:5分钟教你将现有API快速接入扣子商店

    Coze插件开发实战:5分钟教你将现有API快速接入扣子商店

    2026年3月14日
    2
  • Pycharm运行项目代码时输入可选参数

    Pycharm运行项目代码时输入可选参数当我们在跑神经网络的代码时 需要输入一些可选参数 比如权重文件 数据路径等一些参数时 就需要在 pycharm 输入参数 下面是输入参数的方法 1 点击 pycharm 右上角的这个按钮 选中 Editconfigur 2 然后在弹出来的界面填写你需要的参数 如下图 3 直接在 Scriptparame 输入参数名字和参数值 不管是路径名还是参数值 都不用加 双引号 4 最后点击 run 直接运行就可以

    2026年3月26日
    3
  • php批量打印快递单,ecshop批量打印快递单的方法

    php批量打印快递单,ecshop批量打印快递单的方法ecshop 批量打印快递单的方法文章作者 网友投稿 发布时间 2009 10 0617 56 12 来源 网络在 ecshop 后台系统中 在订单的详细页面 可以打印快递单 是一些和 ecshop 订单相关的资料 在配送方式列表中 你可以设置他的打印模板 首先 修改 order list htm 加上一按扭 inputname print shoppingtype submitid btnSubm

    2026年3月17日
    2
  • jinfo介绍[通俗易懂]

    jinfo介绍[通俗易懂]1.jinfo1.1简介jinfo用于打印java的配置信息,这些配置信息包括:javasystempropertiesjvm命令行参数通过查看这些配置信息,可以了解java进程的运

    2022年8月1日
    9
  • Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话。不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多高手喜欢把自己的经验写在网上,供大家来学习,我也是从中受惠了很多,在此我深表感谢。可是我发现我却从来没有将自己平时的一些心得拿出来与大家分享,共同学习,太没有奉献精神了。于是我痛定思痛,决定从今天开始写博客,希望可以指点在我后面的开发者,更快地进入Android开发者的行列当中。好了,废话就说这么多,下面开始

    2022年5月8日
    63

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号