当前位置:网站首页>- Oui. Migration entièrement automatisée de la Sous - base de données des tableaux d'effets sous net

- Oui. Migration entièrement automatisée de la Sous - base de données des tableaux d'effets sous net

2022-07-07 12:53:00 Xue Jiaming

.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éro

Table des matières

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-firstEtShardingCore 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 ,ShardingCoreIl 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+AttributeC'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 IShardingRuntimeContextInjection

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

原网站

版权声明
本文为[Xue Jiaming]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207071038554408.html