1

저는 Asp.Net Core 및 Entity Framework Core를 사용하여 새로운 REST API를 개발 중입니다. 우리는 수평 데이터베이스 파티셔닝 (샤딩)을 사용하는 레거시 시스템에서 데이터를 포팅 할 것입니다. 나는 EF Core에서 이것을 처리하는 좋은 방법을 생각하려고 노력하고있다. 이전의 샤딩 전략에는 중앙 프라임 데이터베이스와 여러 고객 데이터베이스가 포함되었습니다. 모든 검색어에 CustomerId이 포함되었습니다. Prime 데이터베이스에 CustomerId를 쿼리하여 특정 고객의 데이터가 포함 된 고객 데이터베이스를 확인합니다. 데이터베이스 스키마는 다음과 같이보고 :엔티티 프레임 워크 코어를 사용하여 전략 나누기

프라임 데이터베이스

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

컨트롤러 쿼리 작업

[HttpGet] 
[Route("/api/Customers/{customerId}/Orders/{orderId}")] 
public virtual IActionResult GetOrdersForCustomer([FromRoute]long customerId, [FromRoute] long orderId) 
{ 
    using (var ctx = _shardContextService.GetContextForCustomer(customerId)) 
    { 
     var order = ctx.Orders.Where(o => o.CustomerId == customerId && o.OrderId = orderId).Single(); 
     if (order == null) return NotFound("Unable to find this order."); 
     else return new ObjectResult(order); 
    } 
}