水平分表
代码运行环境
经检验,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
