1

私はAsp.NetコアとEntity Frameworkコアを使用して新しいREST APIを開発しています。水平データベース分割(シャーディング)を使用するレガシー・システムからデータを移植します。私はEFコアでこれを処理する良い方法を考えようとしています。私たちの以前のシャーディング戦略には、Primeデータベースの中心と複数のCustomerデータベースが含まれていました。すべてのクエリにはCustomerIdが含まれています。 PrimeデータベースにCustomerIdを照会して、特定の顧客のデータを含む顧客データベースを特定します。データベース・スキーマは次のようになります:Entity Frameworkコアを使用したシャーディングストラテジー

プライムデータベース

dbo.Database 
     DatabaseId INTEGER 
     ConnectionString VARCHAR(200) 

    dbo.Customer 
     CustomerId BIGINT 
     DatabaseId INTEGER 

顧客データベースを

dbo.Order 
    CustomerId BIGINT 
    OrderId INT 
    ... 

http://foo.com/api/Customers/{CustomerId}/Orders/{OrderId}

ようなものになるだろう順序を取得するための例のREST呼び出し

私は持っている必要がありますCustomerDbContextは、各REST要求と共に動的に決定された接続文字列を使用する。リクエストごとにDbContextの新しいインスタンスを作成する必要がありますか?または、実行時に接続文字列を変更できますか?

新しいDbContextを作成する場合、どうすればよいですか?私が見つけることができるサンプルコードのほとんどは、Startup.csからDependency Injectionを使用してシングルトンDbContextを作成します。

答えて

2

ここに私が思いついたことがあります。それはまだ非常に荒いです、私は本当に提供されるかもしれない批評に感謝します。

dbo.Databaseに "UseForNewCustomer BOOLEAN"を追加しました。私はデータベースマイグレーションを使って、新しいシャードをオンザフライで作成しています。

ShardDbContextFactory

public class ShardDbContextFactory : IDbContextFactory<ShardDbContext> 
{ 
    public ShardDbContext Create(DbContextFactoryOptions opts) 
    { 
     return this.Create("This-Connection-String-Isn't-Used"); 
    } 

    public ShardDbContext Create(string connectionString) 
    { 
     var optsBldr = new DbContextOptionsBuilder<ShardDbContext>(); 
     //This is for PostGres. If using MS Sql Server, use 'UseSqlServer()' 
     optsBldr.UseNpgsql(connectionString); 
     return new ShardDbContext(optsBldr.Options); 
    } 
} 

ShardContextService.cs新シャード

[HttpPost] 
public IActionResult Shard([FromBody] string connectionString) { 
    try { 
     _shardContextService.ActivateShard(connectionString); 
     return Ok("Shard activated"); 
    } catch (System.Exception e) { 
     return StatusCode(500, e); 
    } 
} 
を作成するための

public interface IShardContextService { 
    ShardDbContext GetContextForCustomer(int customerId); 
    void ActivateShard(string connectionString, string dbType); 
} 

public class ShardContextService : IShardContextService { 
    private readonly PrimeDbContext _primeContext; 
    public ShardContextService(SystemDbContext primeContext) { 
     _primeContext = primeContext; 
    } 

    public CustomerDbContext GetContextForCustomer(int customerId) 
    { 
     Database shard = null; 
     var customer = _primeContext.Customers 
      .Include(m=>m.Database) 
      .SingleOrDefault(c=>c.CustomerId == customerId); 
     if (customer == null) 
     { 
      shard = _primeContext.Databases.Single(db=>db.UseForNewCustomer); 

      if (shard == null) throw new System.Exception("Unable to determine shard: This is a new customer, and no shards are designated as useable for new customers."); 

      _primeContext.Customers.Add(new Customer { 
       CustomerId = customerId, 
       DatabaseId = shard.DatabaseId 
      }); 

      _primeContext.SaveChanges(); 
     } 
     else 
     { 
      shard = customer.Database; 
     } 
     return (new ShardDbContextFactory()).Create(shard.ConnectionString) 
    } 

    public void ActivateShard(string connectionString) 
    { 
     using (var customerContext = (new ShardDbContextFactory()).Create(connectionString)) 
     { 
      customerContext.Database.Migrate(); 
     } 

     var previous = _primeContext.Databases.SingleOrDefault(d=>d.UseForNewCustomers); 
     if (previous != null) 
     { 
      previous.UseForNewCustomers = false; 
     } 

     var existing = _primeContext.Databases.SingleOrDefault(d=>d.ConnectionString == connectionString); 
     if (existing != null) 
     { 
      existing.UseForNewCustomers = true; 
     } 
     else 
     { 
      _primeContext.Databases.Add(new Database { 
       ConnectionString = connectionString, 
       UseForNewCustomers = true 
      }); 
     } 
     _primeContext.SaveChanges(); 
    } 
} 

コントローラのアクションを0

コントローラ照会の操作

関連する問題