2017-10-12 4 views
0

저는 Asp.Net MVC 응용 프로그램을 빌드하고 있습니다. 필자는 데이터베이스 액세스를 위해 Entity Framework Core를 사용했습니다. 데이터베이스에 SQLServer를 사용하고 있습니다. 모든 EF Stuff는 컨트롤러 메소드 내부에서 잘 작동합니다.끝점과 DbContext에 동일한 트랜잭션 사용

웹 서버에서 실행할 필요가없는 것들을 처리하는 관련 NServiceBus 서비스 응용 프로그램도 있습니다. 일반적인 일 중 하나는 타임 아웃 후에 작업을 수행하는 것입니다. (예 : 사용자가 웹 사이트에서 작업을 수행하고 30 분 후에 무언가를 수행합니다.)

서비스 버스 설정에 SQL Transport를 사용하고 Entity Framework 데이터와 동일한 데이터베이스를 사용합니다.

웹 서버는 NServiceBus 메시지를 처리 ​​할 필요가 없으므로 Send Only IEndpointInstance가 IOC 컨테이너에 등록되어 있습니다. NServiceBus 메시지를 보내야하는 컨트롤러는 IEndpointInstance를 가져 와서 보낼 수 있습니다.

이 모든 것이 깔끔하게 작동하는 것 같습니다. 컨트롤러는 클라이언트 요청을 처리하고, EF를 사용하여 데이터베이스를 변경하며, 끝점을 사용하여 NServiceBus 메시지를 전송하지만 문제가 있습니다. EF와 NServiceBus가 동일한 SQL 데이터베이스를 사용하여 데이터와 메시지를 저장하는 동안 이러한 작업은 동일한 데이터베이스 트랜잭션의 일부가 아닙니다. 즉, 일이 잘못되면 EF가 데이터베이스에 변경 사항을 저장하지만 NServiceBus 메시지 전송이 완료되지 않고 데이터와 메시지가 일치하지 않는 상태가 될 수 있습니다.

그래서 문제는 어떻게 IEndpointInstance를 EF DbContext 저장과 동일한 트랜잭션 내부로 보내야합니까?

EndPoint Transport를 구성 할 때 사용할 수있는 UseCustomSqlConnectionFactory 확장 메서드가 있습니다. IOC 컨테이너에서 웹 요청 범위에 사용 된 DbContext를 가져 와서 SqlConnection을 추출하고이를 엔드 포인트에 제공하는 팩토리를 제공 할 수있었습니다. 여기에서 문제는 IEndpointInstance.Send에서 반환 된 Task는 .Wait()이 완료되면 보내기를 완료하지 못한다는 것입니다.

IMessageHandlerContext에 포함 된 트랜잭션을 EF와 공유하는 데 대한 많은 문서와 예제가 있지만 End와 함께 트랜잭션을 공유하기 위해 IEndpointInstance를 얻는 것에 대해서는 아무 것도 없습니다.

답변

2

확실히 그렇게 할 수 있습니다. DbContext 위에 UnitOfWork를 구현하고 메시지 처리기 파이프 라인과 통합 할 수 있습니다. 이렇게하면 여러 핸들러가있는 경우 모두 동일한 트랜잭션에 참여하게됩니다. 이 접근법은 전송 전용 엔드 포인트와 함께 작동하고 전송 및 수신 (기본 전송이 지원하는 경우)하지만 전송 전용 엔드 포인트에 대해 다른 시간에 실행하도록 동작을 변경해야합니다 (물론 송신 전용 끝점에 대한 '수신'메시지 없음).

당신이 할 방법이 메시지가 들어올 때 메시지 처리 파이프 라인으로 동작을 주입한다는 것입니다 :

public class UnitOfWorkSetupBehaviorBehavior : Behavior<IIncomingLogicalMessageContext> 
{ 
    public override async Task Invoke(IIncomingLogicalMessageContext context, Func<Task> next) 
    { 
     var uow = new EntityFrameworkUnitOfWork(); 
     context.Extensions.Set(uow); 
     await next().ConfigureAwait(false); //executes the next op in the chain 
     context.Extensions.Remove<EntityFrameworkUnitOfWork>(); 
    } 
} 

그리고 작업의 구현의 실제 단위는 다음과 같이 될 것이다 (그것은 사용주의 주변 거래) :이 모든 너무 어려운 것 같으면

class EntityFrameworkUnitOfWork 
{ 
    MyDataContext context; 

    public MyDataContext GetDataContext(SynchronizedStorageSession storageSession) 
    { 
     if (context == null) 
     { 
      var dbConnection = storageSession.Session().Connection; 
      context = new MyDataContext (dbConnection); 

      //Don't use transaction because connection is enlisted in the TransactionScope 
      context.Database.UseTransaction(null); 

      //Call SaveChanges before completing storage session 
      storageSession.OnSaveChanges(x => context.SaveChangesAsync()); 
     } 
     return context; 
    } 
} 

, 당신은 문서 웹 사이트에서 download 수있는 좋은 샘플이 있습니다.

+0

내가 뭔가를 놓치고 있습니다. IEndpointInstance.Send()를 호출 할 때까지 동작이 호출되지 않습니다. 즉, 작업 단위 (UOW)가 그때까지 확장에 추가되지 않았다는 것을 의미합니다.마찬가지로 Extensions에 액세스하려면 어떻게해야합니까? Send()가 호출되기 전에 가져 오기? –

+0

내 MVC 컨트롤러에서 수행하려는 프로세스는 다음과 같습니다. 1. 비즈니스 데이터 DB 컨텍스트를 쿼리하십시오. 2. 종료 또는 계속 결정 3. 비즈니스 데이터베이스 컨텍스트 업데이트 4. 메시지를 보냅니다. 그래서 종점 인스턴스에서 연결 개체를 사용하여 비즈니스 DB 컨텍스트를 만들려면 어떻게해야합니까? 해당 연결이 송신 될 때까지 동작에 노출되지 않으면 어떻게해야합니까? –

+0

@WilliamLeader 그 경우 TransactionScope 도움이되지 않습니까? 동일한 연결 문자열을 사용하여 메시지 + 비즈니스 데이터가 포함 된 데이터베이스를 공유하는 경우 DTC로 에스컬레이션하지 않고 둘 다 동일한 트랜잭션에 참여하게됩니다. –

관련 문제