13

我目前正在将我 6 岁的 C# 应用程序转换为 .NET Core v3 和 EF Core(也使用 Blazor)。除了分片部分外,大部分都在工作。
我们的应用程序为每个客户创建一个新数据库。我们或多或少地使用了这个代码: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;
        }

上面的代码无法编译,因为数据库对象在 EF Core 中不存在这种方式。TenantContext.Database.EnsureCreated();我想我可以在某处使用它来简化它。但我不知道如何修改方法、删除哪些、更改哪些(以及如何)。

当然,我一直在寻找使用分片和 EF Core 的示例,但找不到。这里有没有人以前在 EF Core 中做过这个并且愿意分享?

当我创建一个新客户端时,我正在专门寻找要放入什么startup.cs以及如何创建一个新的分片/数据库。

4

2 回答 2

7

在 EF.Core 中,只需解析 OnConfiguring 中的分片。例如

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

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

    base.OnConfiguring(optionsBuilder);
}

请注意,如果您有一个返回打开的DbConnections 的服务或工厂,那么您需要在 DbContext.Dispose() 中关闭()/处置()它们。如果您获得连接字符串或关闭的连接,则 DbContext 将负责关闭连接。

ASP.NET Core 最佳实践可能需要ITenantConfiguration在 DbContext 中注入服务或类似的东西。但模式是一样的。只需将注入的服务实例保存到 DbContext 字段并在OnConfiguring.

于 2020-05-06T19:01:18.093 回答
0

对于我正在开发的应用程序,在请求时间之前无法发现所需的分片(例如,知道是什么用户发出请求,然后将该用户路由到他们的数据库)。这意味着OnConfiguring上面提出的解决方案是不可行的。

我通过使用IDbContextFactory<TContext>, 并在其之上定义了一个扩展来解决这个问题,它根据您想要的任何内容设置连接字符串。我相信数据库连接是在 EF 中延迟创建的,您可以设置连接字符串,直到 EF 首先需要实际连接到数据库。

就我而言,它看起来像这样:

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

dbContext.Database.SetConnectionString(connectionString);

缺点是它破坏了数据库抽象(这段代码知道我的数据库是本地 sqllite 实例)。在我的应用程序的这一层中不需要抽象,但如果需要,它是可以解决的。

于 2021-07-29T22:03:04.600 回答