當我們new
一個上下文DbContext
後, 每次執行CURD方式時 ,都會依次呼叫OnConfiguring()
,OnModelCreating()
兩個方法。
OnConfiguring()
我們將用來替換一些服務實現,以支援分表的工作OnModelCreating()
我們將用來重新實現 實體與資料庫表 的對映關係每次呼叫OnModelCreating()
時,會判斷實體與資料庫表的對映關係有沒有改變,如果改變則採用新的對映關係。
判斷是否發生改變,通過替換 IModelCacheKeyFactory
介面的實現來完成。詳情可見:在具有相同 DbContext 型別的多個模型之間進行交替
IModelCacheKeyFactory
實現DbContextBase
是一個DbContext
的實現,,ShardingRule
是DbContextBase
的一個共有屬性。
根據分表規則的不同,每次的對映關係也會不同。
public class DynamicModelCacheKeyFactoryDesignTimeSupport : IModelCacheKeyFactory
{
public object Create(DbContext context, bool designTime)
=> context is DbContextBase dynamicContext
? (context.GetType(), dynamicContext.ShardingRule, designTime)
: (object)context.GetType();
public object Create(DbContext context)
=> Create(context, false);
}
OnConfiguring()
替換介面實現
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
//如果分頁規則有 ,代表需要分頁, 那麼需要替換對應的服務實現
if (!string.IsNullOrEmpty(this.ShardingRule))
{
optionsBuilder.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactoryDesignTimeSupport>();
}
}
ModelCustomizer
實現在每次呼叫 OnModelCreating()
時,方法內部會呼叫實現IModelCustomizer
的 ModelCustomizer.cs
的Customize()
方法,我們可以將對映關係寫在此方法內。
通過繼承實現:
IShardingTypeFinder
是一個型別查詢器,請自行實現。
public class ShardingModelCustomizer : ModelCustomizer
{
public ShardingModelCustomizer(ModelCustomizerDependencies dependencies) : base(dependencies)
{
}
public override void Customize(ModelBuilder modelBuilder, DbContext context)
{
base.Customize(modelBuilder, context);
var dbContextBase = context as DbContextBase;
var shardingTypeFinder = dbContextBase.ServiceProvider.GetService<IShardingTypeFinder>();
//查詢需要重新對映表名的類
var shardingTypes = shardingTypeFinder.FindAll(true);
if (shardingTypes != null && shardingTypes.Count() > 0)
{
if (context is DbContextBase contextBase)
{
if (!string.IsNullOrEmpty(contextBase.ShardingRule))
{
foreach (var type in shardingTypes)
{
switch (contextBase.DbContextOptions.DatabaseType)
{
case DatabaseType.SqlServer:
modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");
break;
case DatabaseType.Sqlite:
modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");
break;
case DatabaseType.MySql:
modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}".ToMySQLName());
break;
case DatabaseType.Oracle:
modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}".ToOracleName());
break;
default:
modelBuilder.Entity(type).ToTable($"{type.Name}_{contextBase.ShardingRule}");
break;
}
}
}
}
}
}
}
OnConfiguring()
替換介面實現
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
//如果分頁規則有 ,代表需要分頁, 那麼需要替換對應的服務實現
if (!string.IsNullOrEmpty(this.ShardingRule))
{
optionsBuilder.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactoryDesignTimeSupport>().ReplaceService<IModelCustomizer, ShardingModelCustomizer>();
}
}
DbContextBase
建構函式修改上文提到了ShardingRule
這個屬性的出現 , 如何給這個屬性賦值呢?
有兩種方式:
public string ShardingRule { get; set; }
public DbContextBase(string shardingRule, DbContextOptions options) : base(options)
{
ShardingRule = shardingRule;
}
IShardingRule
是實現規則名稱的自定義介面,自行實現
protected DbContextBase(DbContextOptions options, IServiceProvider serviceProvider)
: base(options)
{
ShardingRule = (serviceProvider.GetService<IShardingRule>()).GetValue();
}
這裡只介紹建構函式傳參使用方式
DbContextOptionsBuilder<DbContextBase> optionsBuilder = new DbContextOptionsBuilder<DbContextBase>();
optionsBuilder.UseSqlServer("connStr");
var options = optionsBuilder.Options;
using (var dbContext = new DbContextBase("202209", options))
{
//TODO....
}
這裡需要注意的是,跨上下文使用事務必須使用同一個連線,所以optionsBuilder.UseSqlServer(connection);
這裡的寫法改變一下,使用同一連線
DbContextOptionsBuilder<DbContextBase> optionsBuilder = new DbContextOptionsBuilder<DbContextBase>();
IDbConnection connection = new SqlConnection("connStr");
optionsBuilder.UseSqlServer(connection);
var options = optionsBuilder.Options;
using (var dbContext = new DbContextBase("202209", options))
{
using (var transaction =await dbContext.Database.BeginTransactionAsync())
{
using (var dbContext2 = new DbContextBase("202210", options))
{
await dbContext2.Database.UseTransactionAsync(transaction);
//TODO....
transaction.Commit();
}
}
}
EFCore分表的實現大致全是這樣,沒有什麼區別。可以參考一些開源的框架,對現有的系統進行適當的調整,畢竟別人寫的並不一定適合你。希望這篇文章可以幫到你。