Как использовать сегментирование базы данных с EF Core и C# "

В настоящее время я конвертирую свое приложение C# 6-летней давности в.NET Core v3 и EF Core (а также использую Blazor). Большая часть его работает, за исключением части Sharding.
Наше приложение создает новую базу данных для каждого клиента. Мы используем для этого более или менее этот код: https://docs.microsoft.com/en-us/azure/sql-database/sql-database-elastic-scale-use-entity-framework-applications-visual-studio
I Я сейчас пытаюсь преобразовать его в EF Core, но застрял в этой части:

        // C'tor to deploy schema and migrations to a new shard
        protected internal TenantContext(string connectionString)
            : base(SetInitializerForConnection(connectionString))
        {
        }

        // Only static methods are allowed in calls into base class c'tors
        private static string SetInitializerForConnection(string connnectionString)
        {
            // We want existence checks so that the schema can get deployed
            Database.SetInitializer<TenantContext<T>>(new CreateDatabaseIfNotExists<TenantContext<T>>());
            return connnectionString;
        }

        // C'tor for data dependent routing. This call will open a validated connection routed to the proper
        // shard by the shard map manager. Note that the base class c'tor call will fail for an open connection
        // if migrations need to be done and SQL credentials are used. This is the reason for the 
        // separation of c'tors into the DDR case (this c'tor) and the internal c'tor for new shards.
        public TenantContext(ShardMap shardMap, T shardingKey, string connectionStr)
            : base(CreateDDRConnection(shardMap, shardingKey, connectionStr), true /* contextOwnsConnection */)
        {
        }

        // Only static methods are allowed in calls into base class c'tors
        private static DbConnection CreateDDRConnection(ShardMap shardMap, T shardingKey, string connectionStr)
        {
            // No initialization
            Database.SetInitializer<TenantContext<T>>(null);

            // Ask shard map to broker a validated connection for the given key
            var conn = shardMap.OpenConnectionForKey<T>(shardingKey, connectionStr, ConnectionOptions.Validate);
            return conn;
        }

Приведенный выше код не компилируется, потому что объект Database таким образом не существует в EF Core. Я предполагаю, что могу упростить его, используяTenantContext.Database.EnsureCreated();где-то. Но я не могу понять, как изменить методы, какие удалить, какие изменить (и как).

Конечно, я искал пример с использованием сегментирования и EF Core, но не нашел. Кто-нибудь здесь делал это раньше в EF Core и желает поделиться?

Я специально ищу, что вставить startup.cs и как создать новый сегмент / базу данных при создании нового клиента.

3 ответа

В EF.Core просто разрешите сегмент в OnConfiguring. НАПРИМЕР

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    var con = GetTenantConnection(this.tenantName);

    optionsBuilder.UseSqlServer(con,o => o.UseRelationalNulls());

    base.OnConfiguring(optionsBuilder);
}

Обратите внимание, что если у вас есть служба или фабрика, которая возвращает открытые DbConnections, вам необходимо закрыть ()/Dispose() их в DbContext.Dispose(). Если вы получили строку подключения или закрытое соединение, DbContext позаботится о закрытии соединения.

Лучшие практики ASP.NET Core, вероятно, требуют внедрения ITenantConfigurationservice или что-то подобное в вашем DbContext. Но картина та же. Просто сохраните внедренный экземпляр службы в поле DbContext и используйте его вOnConfiguring.

попробуйте эту библиотеку для базы данных шардинга или таблицядра шардинга

В приложении, над которым я работаю, желаемый сегмент не может быть обнаружен до момента запроса (например, зная, какой пользователь делает запрос, а затем перенаправляет этого пользователя в свою базу данных). Это означало, что предложенное выше решение было нежизнеспособным.

Я работал над этим, используя и определяя поверх него расширение, которое устанавливает строку подключения в зависимости от того, что вы хотите. Я считаю, что соединение с базой данных создается в EF лениво, и вы можете установить строку подключения до тех пор, пока EF не потребуется фактически подключиться к базе данных.

В моем случае это выглядело примерно так:

      var dbContext = _dbContextFactory.CreateDbContext();
var connectionString = $"DataSource={_sqlliteDirectory}/tenant_{tenant.TenantId}.db";

dbContext.Database.SetConnectionString(connectionString);

Обратной стороной является то, что он нарушает абстракцию базы данных (этот код знает, что моя база данных является локальным экземпляром sqllite). На этом уровне моего приложения не было необходимости в абстракции, но, если потребуется, это вполне разрешимо.

Другие вопросы по тегам