使用FluentMigrator为多个微服务提供单一数据库

赏星河
2023-12-01

目录

高级方法

FluentMigrator

版本信息表的架构

数据库对象的默认架构

结论


高级方法

在本文中,我将使用SQL Server作为我的数据库。一般来说,解决方案非常简单。所有微服务都将使用相同的数据库。但是,我们如何确定不会有冲突呢?我们将使用架构。每个微服务将仅在某个特定的数据库架构中创建数据库对象(表、视图、存储过程等),该架构在所有微服务中都是唯一的。为了避免访问其他微服务的数据时出现问题,我们将创建一个单独的登录名和用户,并仅授予他们一个架构的权限。

例如,对于微服务来处理订单,我们可以这样做:

CREATE LOGIN [orders_login] WITH PASSWORD='p@ssw0rd'

execute('CREATE SCHEMA [orders]')

CREATE USER [orders_user] FOR LOGIN [orders_login] WITH DEFAULT_SCHEMA=[orders]

GRANT CREATE TABLE to [orders_user]
GRANT ALTER,DELETE,SELECT,UPDATE,INSERT,REFERENCES ON SCHEMA :: [orders] to [orders_user]

现在我们准备创建数据库对象。

FluentMigrator

我将使用FluentMigrator NuGet包来修改数据库的结构。它使用起来非常简单。首先配置它:

var serviceProvider = new ServiceCollection()
    .AddFluentMigratorCore()
    .ConfigureRunner(
        builder =>
        {
            builder
                .AddSqlServer2016()
                .WithGlobalConnectionString(connectionString)
                .ScanIn(typeof(Database).Assembly).For.Migrations();
        })
    .BuildServiceProvider();

在这里,我们使用SQL Server 2016或更高版本。connectionString变量包含到我们数据库的连接字符串。Database类型可以是包含迁移的程序集内的任何类型。等!但什么是迁移?

这就是我们描述要对数据库进行的更改的方式。每个迁移都是一个继承Migration的简单类:

[Migration(1)]
public class FirstMigration : Migration
{
    public const string TableName = "orders";   

    public override void Up()
    {
        Create.Table(TableName)
            .WithColumn("id").AsInt32().PrimaryKey().Identity()
            .WithColumn("code").AsString(100).NotNullable();
    }

    public override void Down()
    {
        Delete.Table(TableName);
    }
}

UpDown方法中,您可以描述在应用和回滚迁移时要执行的操作。Migration属性包含一个数字,该数字指定应用迁移的顺序。

现在,将迁移应用于数据库非常简单:

var runner = serviceProvider.GetRequiredService<IMigrationRunner>();

runner.MigrateUp();

就这样。现在必须将所有迁移应用于数据库。FluentMigrator还将创建包含有关所有当前应用的迁移的信息的VersionInfo表。借助此表,FluentMigrator下次将知道应将哪些迁移额外应用于数据库。

不幸的是,对于我们的用例,它不是这样工作的。有两个问题。

首先,VersionInfo表是默认在dbo架构中创建的。但这对我们来说是不可接受的。每个微服务必须在自己的架构中具有自己的VersionInfo表。

第二个问题如下。请考虑以下迁移代码:

Create.Table("orders")

不幸的是,此代码在sdbo架构内也创建了order表。当然,我们可以显式指定模式:

Create.Table("orders").InSchema("orders")

但我宁愿避免这种情况。有人会忘记编写此架构,我们可能会遇到错误。我想替换整个微服务的默认架构。

版本信息表的架构

VersionInfo表设置自定义架构非常容易:

var serviceProvider = new ServiceCollection()
    .AddSingleton<IConventionSet>(new DefaultConventionSet("orders", null))
    .AddFluentMigratorCore()
    .ConfigureRunner(
        builder =>
        {
            builder
                .AddSqlServer2016()
                .WithGlobalConnectionString(connectionString)
                .ScanIn(typeof(Database).Assembly).For.Migrations();
        })
    .BuildServiceProvider();

在这里,我们只需为IConventionSet接口注册一个DefaultConventionSet类的新实例,该实例具有相应的模式。现在我们的VersionInfo表将在orders架构中创建。

数据库对象的默认架构

不幸的是,要理解如何替换其他数据库对象的默认模式并不容易。我花了一些时间。让我们从AddSqlServer2016方法的代码开始。它注册SqlServer2008Quoter类的实例。此类从SqlServer2005Quoter类继承QuoteSchemaName方法。在这里,您可以看到默认架构的来源。

我们将用我们自己的类替换这个Quoter类:

sealed class Quoter : SqlServer2008Quoter
{
    private readonly string _defaultSchemaName;

    public Quoter(string defaultSchemaName)
    {
        if (string.IsNullOrWhiteSpace(defaultSchemaName))
            throw new ArgumentException("Value cannot be null or whitespace.", 
                                         nameof(defaultSchemaName));
        _defaultSchemaName = defaultSchemaName;
    }           

    public override string QuoteSchemaName(string schemaName)
    {
        if (string.IsNullOrEmpty(schemaName))
            return $"[{_defaultSchemaName}]";
        return base.QuoteSchemaName(schemaName);
    }
}

如您所见,这非常简单。实现与SqlServer2005Quoter类中的实现几乎相同,但我们使用的不是dbo,而是自定义模式。

现在我们只需要注册这个类:

var serviceProvider = new ServiceCollection()
    .AddSingleton<IConventionSet>(new DefaultConventionSet("orders", null))
    .AddFluentMigratorCore()
    .ConfigureRunner(
        builder =>
        {
            builder
                .AddSqlServer2016()
                .WithGlobalConnectionString(connectionString)
                .ScanIn(typeof(Database).Assembly).For.Migrations();

            builder.Services.RemoveAll<SqlServer2008Quoter>()
                .AddSingleton<SqlServer2008Quoter>(new Quoter("orders"));
        })
    .BuildServiceProvider();

一切都像我们预期的那样正常。

结论

我希望这篇文章对您有用。很难理解如何更改数据库对象的默认架构。我希望我为你节省了一些时间。

https://www.codeproject.com/Articles/5334167/Single-Database-for-Multiple-Microservices-with

 类似资料: