我目前正在将6年前的C#应用程序转换为.NET Core v3和EF Core(同时使用Blazor(。除了Sharding部分外,大部分都在工作
我们的应用程序为每个客户端创建一个新的数据库。我们或多或少使用以下代码:https://learn.microsoft.com/en-us/azure/sql-database/sql-database-elastic-scale-use-entity-framework-applications-visual-studio
我现在正试图将其转换为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的例子,但没有找到。这里有没有人在英孚核心做过这样的事,并愿意分享?
我特别想知道在startup.cs
中放什么,以及在创建新客户端时如何创建新的分片/数据库。
在EF.Core中,只需在OnConfiguring中解析碎片。例如
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var con = GetTenantConnection(this.tenantName);
optionsBuilder.UseSqlServer(con,o => o.UseRelationalNulls());
base.OnConfiguring(optionsBuilder);
}
请注意,如果您有一个返回openDbConnections的服务或工厂,那么您需要在DbContext.Dispose((中关闭((/Dispose(。
ASP.NET核心的最佳实践可能需要在DbContext中注入ITenantConfiguration
服务或类似服务。但模式是一样的。只需将注入的服务实例保存到DbContext字段中,并在OnConfiguring
中使用它。
对于我正在开发的应用程序,直到请求时才能发现所需的碎片(例如,知道是哪个用户发出请求,然后将该用户路由到他们的数据库(。这意味着上面提出的OnConfiguring
解决方案是不可行的。
我使用IDbContextFactory<TContext>
解决了这个问题,并在它上面定义了一个扩展,它可以根据您想要的设置连接字符串。我相信数据库连接是在EF中延迟创建的,并且您可以设置连接字符串,直到EF第一次需要实际连接到数据库为止。
就我而言,它看起来像这样:
var dbContext = _dbContextFactory.CreateDbContext();
var connectionString = $"DataSource={_sqlliteDirectory}/tenant_{tenant.TenantId}.db";
dbContext.Database.SetConnectionString(connectionString);
缺点是它破坏了数据库抽象(这段代码知道我的数据库是本地sqllite实例(。在我的应用程序的这一层中,抽象是不必要的,但如果需要的话,它是可以解决的。