.NetSous - base de sous - table entièrement automatisée pour la productivité limite inférieureMigrations Code-First
## Introduction Cet articleShardinfCoreVersionx.6.x.x+ Les protagonistes de ce numéro: - [`ShardingCore`](https://github.com/dotnetcore/sharding-core) Section Ief-coreHaute performance inférieure、Solution légère pour la séparation lecture - écriture des sous - bibliothèques de table,Avec une dépendance zéro、Aucun coût d'apprentissage、Adaptateur d'intrusion de code de service zéroTable des matières
- C'est parti.
- Supprimer le conteneur statique
- Primitiveefcore
- IntégrationAbpVNext
- IntégrationFurion
- IntégrationWTM
C'est parti.
Cette fois, notre thème est la productivité extrême,Toutes les sous - bases de données de sous - tables dans d'autres langues sont entièrement automatiséesMigrations Code-First Plus efcore Sous - table sous - base développement insensible
Rappelez - vous la dernière fois que vous avez posté un blog ou la dernière fois, Comment la compatibilité a - t - elle été publiée pour la dernière fois? WTM
Après le cadre, il y a aussi beaucoup de petits partenaires qui viennent me poser des questions sur la compatibilité, comment migrer, etc , Après tant de compatibilité de cadre, j'ai moi - même reconnu quelques problèmes ,Comme dansShardingCore
Utiliser avant l'initialisation (Après toutefcore
) L'initialisation est basée sur l'injection de dépendance sans appel manuel à l'initialisation ,Par exemple,efcore.tool
Problèmes de migration , Ce projet ne peut pas être migré ,Parce queefcore.tool
Ne pas appeler lors de l'utilisation de la commande Configure
Impossible d'initialiser bug, La migration doit passer par un nouveau programme de console , Ne peut pas être migré dans le cadre de ce projet ,Oucode-first
EtShardingCore
Un conflit de paramètres de démarrage a nécessité une modification triviale , Et la Sous - bibliothèque n'est pas prise en charge , J'ai déjà eu un petit ami. 300 C'est un vrai casse - tête si la migration automatique ne fonctionne pas , Bien que ces questions soient en fait des petites choses pour les sous - bases et les sous - tables , Mais une fois que la Sous - base de table atteint un certain niveau, il sera difficile de maintenir .Alors...ShardingCore
Une nouvelle version a été lancée au cours des trois dernières semaines , La nouvelle version traite principalement des points de douleur ci - dessus et normalise l'utilisation du Code
Les logiciels de développement sont généralement disponibles en premier , Et ça marche , Normalisation finale ,ShardingCore
Il en va de même pour, Parce qu'une extension est nécessaire efcore
Donc parfois je ne suis pas familier efcore
L'accès à l'injection ne peut être étendu que par des classes statiques , Et les classes statiques sont en fait une utilisation très atypique ,Sauf en dernier recours..Alors la nouvelle versionx.6.x.x ShardingCore Regardez en bas.
Supprimer le conteneur statique
L'utilisation de conteneurs statiques entraîne ShardingCore Il n'y a qu'une seule donnée tout au long du cycle de déclaration de la demande , Les données sont donc partagées, ce qui est très désavantageux pour les extensions de maintenance d'essai subséquentes , Il n'y a pas d'isolement. , Alors enlevez ShardingContainer
,En fournissantIShardingRuntimeContext
Pour garantir l'accès aux structures paramétriques précédentes ,Le mêmeDbContext Les types utilisent différents IShardingRuntimeContext
Après cela, vous pouvez afficher différentes propriétés de sous - table et de sous - bibliothèque .
Primitiveefcore
Tout d'abord, nous avons ciblé efcore
Extension pour atteindre la Sous - base de données et la Sous - table +code-first Développement de la migration automatique
Ajouter une dépendance ShardingCore 6.6.0.3 MySql
//Veuillez installer la dernière version actuellex.6.0.3+,Premier numéro de version6ReprésentantefcoreNuméro de version
Install-Package ShardingCore -Version 6.6.0.3
Install-Package Pomelo.EntityFrameworkCore.MySql -Version 6.0.1
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.6
Créer untodoEntité
public class TodoItem
{
public string Id { get; set; }
public string Text { get; set; }
}
Créationdbcontext
Il suffit de cartographier l'objet et la base de données, bien sûr DbSet
+Attribute
C'est bon aussi
public class MyDbContext:AbstractShardingDbContext,IShardingTableDbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
public IRouteTail RouteTail { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<TodoItem>(mb =>
{
mb.HasKey(o => o.Id);
mb.Property(o => o.Id).IsRequired().HasMaxLength(50).HasComment("id");
mb.Property(o => o.Text).IsRequired().HasMaxLength(256).HasComment("Les choses");
mb.ToTable(nameof(TodoItem));
});
}
}
Nouvelle route de sous - base et de sous - table
Routage des sous - bibliothèques
public class TodoItemDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<TodoItem,string>
{
/// <summary>
/// idDehashcodePrendre le reste du moule3Sous - base
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public override string ShardingKeyToDataSourceName(object shardingKey)
{
if (shardingKey == null) throw new InvalidOperationException("sharding key cant null");
var stringHashCode = ShardingCoreHelper.GetStringHashCode(shardingKey.ToString());
return $"ds{(Math.Abs(stringHashCode) % 3)}";//ds0,ds1,ds2
}
private readonly List<string> _dataSources = new List<string>() { "ds0", "ds1", "ds2" };
public override List<string> GetAllDataSourceNames()
{
return _dataSources;
}
public override bool AddDataSourceName(string dataSourceName)
{
throw new NotImplementedException();
}
/// <summary>
/// idSous - base
/// </summary>
/// <param name="builder"></param>
public override void Configure(EntityMetadataDataSourceBuilder<TodoItem> builder)
{
builder.ShardingProperty(o => o.Id);
}
public override Func<string, bool> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = ShardingKeyToDataSourceName(shardingKey);
switch (shardingOperator)
{
case ShardingOperatorEnum.Equal: return tail => tail == t;
default:
{
return tail => true;
}
}
}
}
Routage par table
public class TodoItemTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<TodoItem>
{
public TodoItemTableRoute() : base(2, 3)
{
}
/// <summary>
/// Normalement, le contenu n'est pas utilisé pour les touches de partition parce qu'il y a une prémisse pour que les touches de partition ne soient pas modifiées
/// </summary>
/// <param name="builder"></param>
public override void Configure(EntityMetadataTableBuilder<TodoItem> builder)
{
builder.ShardingProperty(o => o.Text);
}
}
Nouvelle génération de scripts de base de données de migration
public class ShardingMySqlMigrationsSqlGenerator:MySqlMigrationsSqlGenerator
{
private readonly IShardingRuntimeContext _shardingRuntimeContext;
public ShardingMySqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider annotationProvider, IMySqlOptions options,IShardingRuntimeContext shardingRuntimeContext) : base(dependencies, annotationProvider, options)
{
_shardingRuntimeContext = shardingRuntimeContext;
}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
var oldCmds = builder.GetCommandList().ToList();
base.Generate(operation, model, builder);
var newCmds = builder.GetCommandList().ToList();
var addCmds = newCmds.Where(x => !oldCmds.Contains(x)).ToList();
MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);
}
}
Configurer l'injection dépendante
ILoggerFactory efLogger = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
});
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddShardingDbContext<MyDbContext>()
.UseRouteConfig(op =>
{
op.AddShardingTableRoute<TodoItemTableRoute>();
op.AddShardingDataSourceRoute<TodoItemDataSourceRoute>();
})
.UseConfig((sp,op) =>
{
op.UseShardingQuery((con, b) =>
{
b.UseMySql(con, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger);
});
op.UseShardingTransaction((con, b) =>
{
b.UseMySql(con, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger);
});
op.AddDefaultDataSource("ds0", "server=127.0.0.1;port=3306;database=mydb0;userid=root;password=root;");
op.AddExtraDataSource(sp=>new Dictionary<string, string>()
{
{"ds1", "server=127.0.0.1;port=3306;database=mydb1;userid=root;password=root;"},
{"ds2", "server=127.0.0.1;port=3306;database=mydb2;userid=root;password=root;"}
});
op.UseShardingMigrationConfigure(b =>
{
b.ReplaceService<IMigrationsSqlGenerator, ShardingMySqlMigrationsSqlGenerator>();
});
}).AddShardingCore();
var app = builder.Build();
// Configure the HTTP request pipeline.
// S'il y a des tâches chronométrées qui doivent être divisées dans le temps, sinon elles ne peuvent pas être ajoutées
app.Services.UseAutoShardingCreate();
using (var scope = app.Services.CreateScope())
{
var defaultShardingDbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
if (defaultShardingDbContext.Database.GetPendingMigrations().Any())
{
defaultShardingDbContext.Database.Migrate();
}
}
// Ceci peut être ajouté si vous avez besoin de numériser les tables après le démarrage et de les numériser
//app.Services.UseAutoTryCompensateTable();
//......
app.Run();
Ajouter un fichier de migration
Add-Migration Init
Programme de démarrage
Migration automatique des tables et des bibliothèques
crud
Ajoutertodo Champs et migration
Ensuite, nous allons cibler TodoItemAjouter unname Champ et ajouter une table qui n'est ni sous - base ni sous - table, puis migrer
public class TodoItem
{
public string Id { get; set; }
public string Text { get; set; }
public string Name { get; set; }
}
public class TodoTest
{
public string Id { get; set; }
public string Test { get; set; }
}
//docontext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<TodoItem>(mb =>
{
mb.HasKey(o => o.Id);
mb.Property(o => o.Id).IsRequired().HasMaxLength(50).HasComment("id");
mb.Property(o => o.Text).IsRequired().HasMaxLength(256).HasComment("Les choses");
mb.Property(o => o.Name).HasMaxLength(256).HasComment("Nom");
mb.ToTable(nameof(TodoItem));
});
modelBuilder.Entity<TodoTest>(mb =>
{
mb.HasKey(o => o.Id);
mb.Property(o => o.Id).IsRequired().HasMaxLength(50).HasComment("id");
mb.Property(o => o.Test).IsRequired().HasMaxLength(256).HasComment("Tests");
mb.ToTable(nameof(TodoTest));
});
}
Sans surprise, on a réussi et on a recommencé
Après le démarrage du programme, nous avons été surpris de constater que non seulement le tableau original a été ajouté nameChamp, Et des tableaux non séparés pour les tranches ont été ajoutés
C'est fini.efcore
Sous - base de données native + Migration entièrement automatisée Code-FirstTerminé., Cela non seulement améliore considérablement les performances du programme, mais facilite également la maintenance des développeurs .
IntégrationAbpVNext
C'est fait.efcore
Migration native de la Sous - base de table nous allons faire abpOpération suivante
Commençons pargithubEn basabp-samples Télécharger à l'intérieur demoTests,Choisissez icitodo-mvc
Ensuite, nous ouvrons les dépendances d'installation localement ,Il suffit d'installer·ShardingCore· 6.6.0.3.
Créer deux nouvelles interfaces pour assigner le temps de création et guid
Parce queShardingCoreBesoinadd,update,removeQuandshardingkeyNe peut pas être vide, Vous pouvez assigner vos propres valeurs ,Mais comme çaefcore Certaines caractéristiques ne fonctionneront pas , Donc nous avons fait la compatibilité
//InTodoApp.Domain.Shared Ajouter deux Interfaces (Non.)
public interface IShardingKeyIsCreationTime
{
}
public class IShardingKeyIsGuId
{
}
AbpDbContextClasse abstraite
Parce queAbpHéritage requisAbpDbContext Il y a donc un changement ici parce que ShardingCore Il n'y a qu'une interface nécessaire pour répondre à n'importe quelle situation
// Le reste du Code, à l'exception de la plupart, peut être déplacé à la fin du texte demoVoir ici
public abstract class AbstractShardingAbpDbContext<TDbContext> : AbpDbContext<TDbContext>, IShardingDbContext, ISupportShardingReadWrite
where TDbContext : DbContext
{
private readonly IShardingDbContextExecutor _shardingDbContextExecutor;
protected AbstractShardingAbpDbContext(DbContextOptions<TDbContext> options) : base(options)
{
var wrapOptionsExtension = options.FindExtension<ShardingWrapOptionsExtension>();
if (wrapOptionsExtension != null)
{
_shardingDbContextExecutor = new ShardingDbContextExecutor(this);
}
}
public DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail)
{
var dbContext = _shardingDbContextExecutor.CreateDbContext(strategy, dataSourceName, routeTail);
if (dbContext is AbpDbContext<TDbContext> abpDbContext && abpDbContext.LazyServiceProvider == null)
{
abpDbContext.LazyServiceProvider = this.LazyServiceProvider;
}
return dbContext;
}
}
Ajouter un routage de sous - base et de sous - table
todoitem idSous - base d'accès aux modules
public class TodoDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<TodoItem,string>
{
public override string ShardingKeyToDataSourceName(object shardingKey)
{
if (shardingKey == null) throw new InvalidOperationException("sharding key cant null");
var stringHashCode = ShardingCoreHelper.GetStringHashCode(shardingKey.ToString());
return $"ds{(Math.Abs(stringHashCode) % 3)}";//ds0,ds1,ds2
}
public override List<string> GetAllDataSourceNames()
{
return new List<string>()
{
"ds0", "ds1", "ds2"
};
}
public override bool AddDataSourceName(string dataSourceName)
{
throw new NotImplementedException();
}
public override void Configure(EntityMetadataDataSourceBuilder<TodoItem> builder)
{
builder.ShardingProperty(o => o.Id);
}
public override Func<string, bool> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = ShardingKeyToDataSourceName(shardingKey);
switch (shardingOperator)
{
case ShardingOperatorEnum.Equal: return tail => tail == t;
default:
{
return tail => true;
}
}
}
}
todoitem text Tableau de séparation des moisissures
public class TodoTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<TodoItem>
{
public TodoTableRoute() : base(2, 5)
{
}
public override void Configure(EntityMetadataTableBuilder<TodoItem> builder)
{
builder.ShardingProperty(o => o.Text);
}
}
Compilationsqlserver Génération de scripts de migration fragmentés
public class ShardingSqlServerMigrationsSqlGenerator: SqlServerMigrationsSqlGenerator
{
private readonly IShardingRuntimeContext _shardingRuntimeContext;
public ShardingSqlServerMigrationsSqlGenerator(IShardingRuntimeContext shardingRuntimeContext,[NotNull] MigrationsSqlGeneratorDependencies dependencies, [NotNull] IRelationalAnnotationProvider migrationsAnnotations) : base(dependencies, migrationsAnnotations)
{
_shardingRuntimeContext = shardingRuntimeContext;
}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
var oldCmds = builder.GetCommandList().ToList();
base.Generate(operation, model, builder);
var newCmds = builder.GetCommandList().ToList();
var addCmds = newCmds.Where(x => !oldCmds.Contains(x)).ToList();
MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);
}
}
abpDeefcoreInjection du module
TodoAppEntityFrameworkCoreModule
Écrire l'injection
public class TodoAppEntityFrameworkCoreModule : AbpModule
{
public static readonly ILoggerFactory efLogger = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
});
public override void PreConfigureServices(ServiceConfigurationContext context)
{
TodoAppEfCoreEntityExtensionMappings.Configure();
}
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddAbpDbContext<TodoAppDbContext>(options =>
{
/* Remove "includeAllEntities: true" to create
* default repositories only for aggregate roots */
options.AddDefaultRepositories(includeAllEntities: true);
});
Configure<AbpDbContextOptions>(options =>
{
/* The main point to change your DBMS.
* See also TodoAppDbContextFactory for EF Core tooling. */
options.UseSqlServer();
options.Configure<TodoAppDbContext>(innerContext =>
{
ShardingCoreExtension.UseDefaultSharding<TodoAppDbContext>(innerContext.ServiceProvider, innerContext.DbContextOptions);
});
});
context.Services.AddShardingConfigure<TodoAppDbContext>()
.UseRouteConfig(op =>
{
op.AddShardingDataSourceRoute<TodoDataSourceRoute>();
op.AddShardingTableRoute<TodoTableRoute>();
})
.UseConfig((sp, op) =>
{
//var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
op.UseShardingQuery((conStr, builder) =>
{
builder.UseSqlServer(conStr).UseLoggerFactory(efLogger);
});
op.UseShardingTransaction((connection, builder) =>
{
builder.UseSqlServer(connection).UseLoggerFactory(efLogger);
});
op.UseShardingMigrationConfigure(builder =>
{
builder.ReplaceService<IMigrationsSqlGenerator, ShardingSqlServerMigrationsSqlGenerator>();
});
op.AddDefaultDataSource("ds0", "Server=.;Database=TodoApp;Trusted_Connection=True");
op.AddExtraDataSource(sp =>
{
return new Dictionary<string, string>()
{
{ "ds1", "Server=.;Database=TodoApp1;Trusted_Connection=True" },
{ "ds2", "Server=.;Database=TodoApp2;Trusted_Connection=True" }
};
});
})
.AddShardingCore();
}
public override void OnPostApplicationInitialization(ApplicationInitializationContext context)
{
base.OnPostApplicationInitialization(context);
// S'il y a des tâches programmées pour créer une table qui doivent être créées par le système selon le routage par défaut du système, n'oubliez pas de les ouvrir
context.ServiceProvider.UseAutoShardingCreate();
// Tableau des indemnités // La migration automatique n'est pas nécessaire
//context.ServiceProvider.UseAutoTryCompensateTable();
}
}
DémarrageabpProjet de migration
Démarrage
Attendre la sortie
Insérertodoitem
Requête
Validation
Jusqu'à présent, nous avons terminé de cibler abpvnext Sous - table et sous - bibliothèque de + Automatiser les opérations de migration
IntégrationFurion
Ensuite, nous commençons à intégrerFurionFonctionnement
D'abord, installez toujours les dépendances
Ajouter une dépendance ShardingCore 6.6.0.3 MySql
Install-Package Furion -Version 3.7.5
//Veuillez installer la dernière version actuellex.6.0.5+,Premier numéro de version6ReprésentantefcoreNuméro de version
Install-Package ShardingCore -Version 6.6.0.5
Install-Package Pomelo.EntityFrameworkCore.MySql -Version 6.0.1
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.6
Nouveautodoitem
public class TodoItem:IEntity, IEntityTypeBuilder<TodoItem>
{
public string Id { get; set; }
public string Text { get; set; }
public void Configure(EntityTypeBuilder<TodoItem> entityBuilder, DbContext dbContext, Type dbContextLocator)
{
entityBuilder.HasKey(o => o.Id);
entityBuilder.Property(o => o.Id).IsRequired().HasMaxLength(50).HasComment("id");
entityBuilder.Property(o => o.Text).IsRequired().HasMaxLength(256).HasComment("Les choses");
entityBuilder.ToTable(nameof(TodoItem));
}
}
Ajout d'un DbContextEtAbpC'est pareil
Les objets abstraits regardent - ils loin? , Ajoutez - en un ici. dbcontext
public class MyDbContext : AppShardingDbContext<MyDbContext>,IShardingTableDbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
public IRouteTail RouteTail { get; set; }
}
Ajouter un nouveau routage de sous - base de données de sous - table
Nouvelle route de sous - base
public class TodoItemDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<TodoItem,string>
{
/// <summary>
/// idDehashcodePrendre le reste du moule3Sous - base
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public override string ShardingKeyToDataSourceName(object shardingKey)
{
if (shardingKey == null) throw new InvalidOperationException("sharding key cant null");
var stringHashCode = ShardingCoreHelper.GetStringHashCode(shardingKey.ToString());
return $"ds{(Math.Abs(stringHashCode) % 3)}";//ds0,ds1,ds2
}
private readonly List<string> _dataSources = new List<string>() { "ds0", "ds1", "ds2" };
public override List<string> GetAllDataSourceNames()
{
return _dataSources;
}
public override bool AddDataSourceName(string dataSourceName)
{
throw new NotImplementedException();
}
/// <summary>
/// idSous - base
/// </summary>
/// <param name="builder"></param>
public override void Configure(EntityMetadataDataSourceBuilder<TodoItem> builder)
{
builder.ShardingProperty(o => o.Id);
}
public override Func<string, bool> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = ShardingKeyToDataSourceName(shardingKey);
switch (shardingOperator)
{
case ShardingOperatorEnum.Equal: return tail => tail == t;
default:
{
return tail => true;
}
}
}
}
Nouvelle route de sous - table
public class TodoItemTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<TodoItem>
{
public TodoItemTableRoute() : base(2, 3)
{
}
/// <summary>
/// Normalement, le contenu n'est pas utilisé pour les touches de partition parce qu'il y a une prémisse pour que les touches de partition ne soient pas modifiées
/// </summary>
/// <param name="builder"></param>
public override void Configure(EntityMetadataTableBuilder<TodoItem> builder)
{
builder.ShardingProperty(o => o.Text);
}
}
Écrire un fichier de migration
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure.Internal;
using Pomelo.EntityFrameworkCore.MySql.Migrations;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Helpers;
namespace TodoApp;
public class ShardingMySqlMigrationsSqlGenerator:MySqlMigrationsSqlGenerator
{
private readonly IShardingRuntimeContext _shardingRuntimeContext;
public ShardingMySqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider annotationProvider, IMySqlOptions options,IShardingRuntimeContext shardingRuntimeContext) : base(dependencies, annotationProvider, options)
{
_shardingRuntimeContext = shardingRuntimeContext;
}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
var oldCmds = builder.GetCommandList().ToList();
base.Generate(operation, model, builder);
var newCmds = builder.GetCommandList().ToList();
var addCmds = newCmds.Where(x => !oldCmds.Contains(x)).ToList();
MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);
}
}
Injection de démarrage
Un coup d'oeil. furion
Il ne semble pas que Func<IServiceProvider,DbContextOptionBuilder>
Deefcore
Le mode d'injection doit donc être statique ,
Si une approche statique nécessite la mise en œuvre d'une interface IDbContextCreator
//Création statiqueIShardingRuntimeContext
public class ShardingCoreProvider
{
private static ILoggerFactory efLogger = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
});
private static readonly IShardingRuntimeContext instance;
public static IShardingRuntimeContext ShardingRuntimeContext => instance;
static ShardingCoreProvider()
{
instance=new ShardingRuntimeBuilder<MyDbContext>().UseRouteConfig(op =>
{
op.AddShardingTableRoute<TodoItemTableRoute>();
op.AddShardingDataSourceRoute<TodoItemDataSourceRoute>();
})
.UseConfig((sp,op) =>
{
op.UseShardingQuery((con, b) =>
{
b.UseMySql(con, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger);
});
op.UseShardingTransaction((con, b) =>
{
b.UseMySql(con, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger);
});
op.AddDefaultDataSource("ds0", "server=127.0.0.1;port=3306;database=furion0;userid=root;password=root;");
op.AddExtraDataSource(sp=>new Dictionary<string, string>()
{
{"ds1", "server=127.0.0.1;port=3306;database=furion1;userid=root;password=root;"},
{"ds2", "server=127.0.0.1;port=3306;database=furion2;userid=root;password=root;"}
});
op.UseShardingMigrationConfigure(b =>
{
b.ReplaceService<IMigrationsSqlGenerator, ShardingMySqlMigrationsSqlGenerator>();
});
}).ReplaceService<IDbContextCreator, CustomerDbContextCreator>(ServiceLifetime.Singleton).Build();
}
}
//Démarrer le service
public class ShardingCoreComponent:IServiceComponent
{
public void Load(IServiceCollection services, ComponentContext componentContext)
{
services.AddControllers();
services.AddEndpointsApiExplorer();
services.AddSwaggerGen();
services.AddDatabaseAccessor(options =>
{
// Configurer la base de données par défaut
options.AddDb<MyDbContext>(o =>
{
o.UseDefaultSharding<MyDbContext>(ShardingCoreProvider.ShardingRuntimeContext);
});
});
//Injection dépendante
services.AddSingleton<IShardingRuntimeContext>(sp => ShardingCoreProvider.ShardingRuntimeContext);
}
}
public class CustomerDbContextCreator:ActivatorDbContextCreator<MyDbContext>
{
public override DbContext GetShellDbContext(IShardingProvider shardingProvider)
{
var dbContextOptionsBuilder = new DbContextOptionsBuilder<MyDbContext>();
dbContextOptionsBuilder.UseDefaultSharding<MyDbContext>(ShardingCoreProvider.ShardingRuntimeContext);
return new MyDbContext(dbContextOptionsBuilder.Options);
}
}
public class UseShardingCoreComponent:IApplicationComponent
{
public void Load(IApplicationBuilder app, IWebHostEnvironment env, ComponentContext componentContext)
{
//......
app.ApplicationServices.UseAutoShardingCreate();
var serviceProvider = app.ApplicationServices;
using (var scope = app.ApplicationServices.CreateScope())
{
var defaultShardingDbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>();
if (defaultShardingDbContext.Database.GetPendingMigrations().Any())
{
defaultShardingDbContext.Database.Migrate();
}
}
// app.Services.UseAutoTryCompensateTable();
}
}
//Program
using TodoApp;
Serve.Run(RunOptions.Default
.AddComponent<ShardingCoreComponent>()
.UseComponent<UseShardingCoreComponent>());
Ajouter un fichier de migration
Démarrage
Ajouter, supprimer et modifier la requête
IntégrationWTM
Il y a eu un héritage avant, et il y a eu des problèmes de migration de ce côté ShardingCore Des solutions de migration plus sophistiquées ont été mises au point et utilisées code-first Plus insensible
Ajouter une dépendance
Ajouter une dépendance ShardingCore 6.6.0.3 MySql
//Veuillez installer la dernière version actuellex.6.0.5+,Premier numéro de version6ReprésentantefcoreNuméro de version
Install-Package ShardingCore -Version 6.6.0.5
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.6
Ajouter un nouveau routage de sous - base de données de sous - table
// Routage des sous - bibliothèques
public class TodoDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<Todo,string>
{
/// <summary>
/// idDehashcodePrendre le reste du moule3Sous - base
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public override string ShardingKeyToDataSourceName(object shardingKey)
{
if (shardingKey == null) throw new InvalidOperationException("sharding key cant null");
var stringHashCode = ShardingCoreHelper.GetStringHashCode(shardingKey.ToString());
return $"ds{(Math.Abs(stringHashCode) % 3)}";//ds0,ds1,ds2
}
private readonly List<string> _dataSources = new List<string>() { "ds0", "ds1", "ds2" };
public override List<string> GetAllDataSourceNames()
{
return _dataSources;
}
public override bool AddDataSourceName(string dataSourceName)
{
throw new NotImplementedException();
}
/// <summary>
/// idSous - base
/// </summary>
/// <param name="builder"></param>
public override void Configure(EntityMetadataDataSourceBuilder<Todo> builder)
{
builder.ShardingProperty(o => o.Id);
}
public override Func<string, bool> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = ShardingKeyToDataSourceName(shardingKey);
switch (shardingOperator)
{
case ShardingOperatorEnum.Equal: return tail => tail == t;
default:
{
return tail => true;
}
}
}
}
// Routage par table
public class TodoTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<Todo>
{
public TodoTableRoute() : base(2, 3)
{
}
/// <summary>
/// Normalement, le contenu n'est pas utilisé pour les touches de partition parce qu'il y a une prémisse pour que les touches de partition ne soient pas modifiées
/// </summary>
/// <param name="builder"></param>
public override void Configure(EntityMetadataTableBuilder<Todo> builder)
{
builder.ShardingProperty(o => o.Name);
}
}
CréationDbContextCreator
public class WTMDbContextCreator:IDbContextCreator
{
public DbContext CreateDbContext(DbContext shellDbContext, ShardingDbContextOptions shardingDbContextOptions)
{
var context = new DataContext((DbContextOptions<DataContext>)shardingDbContextOptions.DbContextOptions);
context.RouteTail = shardingDbContextOptions.RouteTail;
return context;
}
public DbContext GetShellDbContext(IShardingProvider shardingProvider)
{
var dbContextOptionsBuilder = new DbContextOptionsBuilder<DataContext>();
dbContextOptionsBuilder.UseDefaultSharding<DataContext>(ShardingCoreProvider.ShardingRuntimeContext);
return new DataContext(dbContextOptionsBuilder.Options);
}
}
Script de migration
public class ShardingMySqlMigrationsSqlGenerator:MySqlMigrationsSqlGenerator
{
private readonly IShardingRuntimeContext _shardingRuntimeContext;
public ShardingMySqlMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IRelationalAnnotationProvider annotationProvider, IMySqlOptions options,IShardingRuntimeContext shardingRuntimeContext) : base(dependencies, annotationProvider, options)
{
_shardingRuntimeContext = shardingRuntimeContext;
}
protected override void Generate(
MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
var oldCmds = builder.GetCommandList().ToList();
base.Generate(operation, model, builder);
var newCmds = builder.GetCommandList().ToList();
var addCmds = newCmds.Where(x => !oldCmds.Contains(x)).ToList();
MigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);
}
}
Construction statiqueIShardingRuntimeContext
Parce queWTMCréationdbcontext Ce n'est pas créé par injection dépendante, mais par le reste de l'implémentation interne, donc pour être compatible, nous ne pouvons le faire que par statique IShardingRuntimeContext
Injection
public class ShardingCoreProvider
{
private static ILoggerFactory efLogger = LoggerFactory.Create(builder =>
{
builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
});
private static readonly IShardingRuntimeContext instance;
public static IShardingRuntimeContext ShardingRuntimeContext => instance;
static ShardingCoreProvider()
{
instance=new ShardingRuntimeBuilder<DataContext>().UseRouteConfig(op =>
{
op.AddShardingTableRoute<TodoRoute>();
op.AddShardingDataSourceRoute<TodoDataSourceRoute>();
})
.UseConfig((sp,op) =>
{
op.UseShardingQuery((con, b) =>
{
b.UseMySql(con, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger);
});
op.UseShardingTransaction((con, b) =>
{
b.UseMySql(con, new MySqlServerVersion(new Version()))
.UseLoggerFactory(efLogger);
});
op.AddDefaultDataSource("ds0", "server=127.0.0.1;port=3306;database=wtm0;userid=root;password=root;");
op.AddExtraDataSource(sp=>new Dictionary<string, string>()
{
{"ds1", "server=127.0.0.1;port=3306;database=wtm1;userid=root;password=root;"},
{"ds2", "server=127.0.0.1;port=3306;database=wtm2;userid=root;password=root;"}
});
op.UseShardingMigrationConfigure(b =>
{
b.ReplaceService<IMigrationsSqlGenerator, ShardingMySqlMigrationsSqlGenerator>();
});
}).ReplaceService<IDbContextCreator, WTMDbContextCreator>(ServiceLifetime.Singleton).Build();
}
}
Créer un fragment abstrait DbContext
Parce qu'il est trop long, il ne montre que la partie principale du reste demoVoir
public abstract class AbstractShardingFrameworkContext:FrameworkContext, IShardingDbContext, ISupportShardingReadWrite
{
protected IShardingDbContextExecutor ShardingDbContextExecutor
{
get;
}
public AbstractShardingFrameworkContext(CS cs)
: base(cs)
{
ShardingDbContextExecutor =new ShardingDbContextExecutor(this);
IsExecutor = false;
}
public AbstractShardingFrameworkContext(string cs, DBTypeEnum dbtype)
: base(cs, dbtype)
{
ShardingDbContextExecutor =new ShardingDbContextExecutor(this);
IsExecutor = false;
}
public AbstractShardingFrameworkContext(string cs, DBTypeEnum dbtype, string version = null)
: base(cs, dbtype, version)
{
ShardingDbContextExecutor =new ShardingDbContextExecutor(this);
IsExecutor = false;
}
public AbstractShardingFrameworkContext(DbContextOptions options) : base(options)
{
var wrapOptionsExtension = options.FindExtension<ShardingWrapOptionsExtension>();
if (wrapOptionsExtension != null)
{
ShardingDbContextExecutor =new ShardingDbContextExecutor(this);;
}
IsExecutor = wrapOptionsExtension == null;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (this.CSName!=null)
{
base.OnConfiguring(optionsBuilder);
optionsBuilder.UseDefaultSharding<DataContext>(ShardingCoreProvider.ShardingRuntimeContext);
}
}
public DbContext GetDbContext(string dataSourceName, CreateDbContextStrategyEnum strategy, IRouteTail routeTail)
{
return ShardingDbContextExecutor.CreateDbContext(strategy, dataSourceName, routeTail);
}
}
Modifierdbcontext
public class DataContextFactory : IDesignTimeDbContextFactory<DataContext>
{
public DataContext CreateDbContext(string[] args)
{
var virtualDataSource = ShardingCoreProvider.ShardingRuntimeContext.GetVirtualDataSource();
var defaultConnectionString = virtualDataSource.DefaultConnectionString;
return new DataContext(defaultConnectionString, DBTypeEnum.MySql);
}
}
InjectionShardingCore
Suppression du Code supplémentaire précédent
public void ConfigureServices(IServiceCollection services){
//....
services.AddSingleton<IShardingRuntimeContext>(sp => ShardingCoreProvider.ShardingRuntimeContext);
}
public void Configure(IApplicationBuilder app, IOptionsMonitor<Configs> configs)
{
IconFontsHelper.GenerateIconFont();
// using (var scope = app.ApplicationServices.CreateScope())
// {
// var requiredService = scope.ServiceProvider.GetRequiredService<WTMContext>();
// var requiredServiceDc = requiredService.DC;
// }
//Tâches programmées
app.ApplicationServices.UseAutoShardingCreate();
using (var dbconContext=new DataContextFactory().CreateDbContext(new string[0]))
{
dbconContext.Database.Migrate();
}
// Compléter le tableau pour empêcher iis Ce genre d'hibernation fait que les tables mensuelles quotidiennes ne sont pas nouvellement créées
//app.ApplicationServices.UseAutoTryCompensateTable();
//....
}
Migration
Programme de démarrage
crud
La dernière.
(ShardingWithFrameWork)[https://github.com/xuejmnet/ShardingWithFramework] https://github.com/xuejmnet/ShardingWithFramework
Vous avez tous vu par ici.starOu est - ce que,Section I.NetSolution de sous - base et de sous - table à apprendre,Simple à comprendre commesharding-jdbcIn.netImplémenter et soutenir plus de fonctionnalités et une meilleure agrégation des données,Ayant des propriétés natives97%,Et pas d'intrusion commerciale,Prise en charge de tout ce qui n'est pas fragmentéefcoreRequête Native