1

두 테이블 인 테이블과 소유자가 있습니다. 두 개의 ID 열이 있지만 하나는 다른 테이블에 대해 필요한 외래 키가 있습니다.EF 5.0 계산 된 아이디 값으로 트랜잭션을 롤백하지 않습니다.

트랜잭션을 동시에 추가해야하지만 기본 테이블에서 삽입이 실패하면 보조 테이블에 대한 레코드를 작성한 트랜잭션을 롤백해야합니다.

내가 아는 한 .SaveChanges()가 보조 테이블에서 자동 생성 된 ID를 가져 오려면 필요하지만이 또한 트랜잭션을 커밋하는 것처럼 보입니다.

다른 방법이 있습니까?

public class HOTEL 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public Int64 HOTEL_ID { get; set; } 

    public string blah1 { get; set; } 

    public string blah2 { get; set; } 
} 

public class OWNER 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public Int64 OWNER_ID { get; set; } 

    public string blah3 { get; set; } 

    public string blah4 { get; set; } 

    public Int64 HOTEL_ID { get; set; } 

    [ForeignKey("HOTEL_ID")] 
    public virtual HOTEL HOTEL { get; set; } 
} 

...

public class MyContext : DbContext 
{ 
    public MyContext() : base() { } 
    public MyContext(string connectionString) : base(connectionString) { } 

    public DbSet<HOTEL> HOTELs { get; set; } 
    public DbSet<OWNER> OWNERs { get; set; } 

    public ObjectContext ObjectContext 
    { 
     get 
     { 
      return (this as IObjectContextAdapter).ObjectContext; 
     } 
    } 
} 

...

Int64 ret = 0; 

// Suppress required for DB2. 
using (var transaction = new TransactionScope(TransactionScopeOption.Suppress)) 
{ 
    try 
    { 
     using (var context = new MyContext()) 
     { 
      var secondaryEntity = new HOTEL(); 
      context.HOTELs.Add(secondaryEntity); 

      // This appears to commit the changes in the trasaction. 
      context.SaveChanges(); 

      primaryEntity.HOTEL_ID = secondaryEntity.HOTEL_ID; 

      context.OWNERs.Attach(primaryEntity); 
      context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified; 

      context.SaveChanges(); 
      ret = primaryEntity.OWNER_ID; 
     } 
    } 
    catch (Exception ex) 
    { 
     // Deal with errors. 
    } 

    if (ret != 0) 
    { 
     transaction.Complete(); 
    } 
} 

return ret; 
+0

을 컨텍스트에 엔티티를 저장하고 변경 사항을 저장 하시겠습니까? SaveChanges는 모든 항목을 트랜잭션에 저장하므로 두 항목 모두 저장되거나 none이됩니다. – Pawel

+0

@Pawel SaveChanges()를 호출하지 않으면 데이터베이스에서 자동 생성 된 ID가 생성되지 않습니다. – midspace

+0

내가 말하려고했던 것은 참조를 올바르게 설정했다는 것입니다. 외래 키를 설정할 필요가 없습니다. (값을 모를 수 있습니다.) 배치를 저장할 때 설정해야합니다. – Pawel

답변

0

는 이상이 작업 후, 내가 TransactionScopeOption.Suppress을 사용했기 때문에 실제 문제가 밝혀졌습니다. DB2가 다른 옵션과 함께 작동하지 않는 것으로 보였기 때문에 이것을 사용하고있었습니다. 이는 Client 9.7 Fixpack 4를 사용하고 있기 때문입니다. 다른 TransactionScopeOption을 사용할 때 다음 오류가 반환되었습니다.

ERROR [57016] [IBM][DB2/NT64] SQL0668N Operation not allowed for reason code "7" on table "DB2ADMIN.HOTEL". 

더 자세히 설명하는 것은 DB2가 TransactionScope에서 부분 분해하지 않기 때문입니다. 이 문제에 대한 몇 가지 다른 문제를 살펴본 후에 다음 단계를 통해 해결되었습니다.

  • XA 트랜잭션 실행 "DCOMCNFG는"구성 요소 서비스 관리 콘솔을 시작하려면 시작 MSDTC 속성
    1. 에서 사용 가능.
    2. 트리를 탐색하여 "구성 요소 서비스> 컴퓨터> 내 컴퓨터> Distributed Transaction Coordinator> 로컬 DTC"로 이동합니다.
    3. "로컬 DTC"에서 마우스 오른쪽 단추를 클릭하고 속성을 선택하십시오.
    4. "보안"탭을 선택하십시오.
    5. "XA 트랜잭션 사용"탭을 선택하십시오.
    6. OK를 누릅니다. 내가 DB2 9.7 FP4를 계속 사용하면
  • , 나는 'DB2CLI.DLL'이름을 사용하여 Microsoft \ MSDTC \ XADLL \ 레지스트리 키 HKLM \ 소프트웨어에서 값을 입력해야하고 값 'C : \ 프로그램 파일 \ "DB2CLI.DLL"= IBM \ SQLLIB \ BIN \ DB2CLI.DLL '

    [마이크로 소프트 \ MSDTC \ XADLL \ HKEY_LOCAL_MACHINE \ 소프트웨어] "C : \ 프로그램 파일 \ IBM \ SQLLIB \ BIN \의 db2cli.dll"

  • (레지스트리 항목에서) 또는 DB2 클라이언트 9.7 FP6을 최소한 설치하십시오.

  • 컴퓨터를 재부팅하십시오.
  • TransactionScope를 폐기하기 전에 연결을 닫지 마십시오.

최종 코드가 이와 같이 나타났습니다.

Int64 ret = 0; 

using (var transaction = new TransactionScope(TransactionScopeOption.Required)) 
{ 
    try 
    { 
     using (var context = new MyContext()) 
     { 
      var secondaryEntity = new HOTEL(); 
      context.HOTELs.Add(secondaryEntity); 

      // SaveChanges(bool) has been depricated, use SaveOptions. 
      // SaveChanges is required to generate the autogenerated Identity HOTEL_ID. 
      context.ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave); 
      primaryEntity.HOTEL_ID = secondaryEntity.HOTEL_ID; 

      context.OWNERs.Attach(primaryEntity); 
      context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified; 

      context.ObjectContext.SaveChanges(SaveOptions.AcceptAllChangesAfterSave); 
      ret = primaryEntity.OWNER_ID; 

      if (ret != 0) 
      { 
       transaction.Complete(); 
       context.ObjectContext.AcceptAllChanges(); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     // Deal with errors. 
    } 
} 

return ret; 

secondaryEntity의 ID를 생성하고 수동으로 값을 할당 할 필요없이 primaryEntity에 자동으로 첨부 할 @Pawel 의견을 읽은 후 대안, 왜 모든 추가하지

Int64 ret = 0; 

using (var transaction = new TransactionScope(TransactionScopeOption.Required)) 
{ 
    try 
    { 
     using (var context = new MyContext()) 
     { 
      var secondaryEntity = new HOTEL(); 
      primaryEntity.HOTEL = secondaryEntity; 

      context.HOTELs.Add(secondaryEntity); 
      context.OWNERs.Attach(primaryEntity); 
      context.Entry(primaryEntity).State = primaryEntity.OWNER_ID == 0 ? EntityState.Added : EntityState.Modified; 

      context.SaveChanges(); 
      ret = primaryEntity.OWNER_ID; 

      // TransactionScopeOption.Required can still used in case something 
      // goes wrong with additional processing at this point. 

      if (ret != 0) 
      { 
       transaction.Complete(); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     // Deal with errors. 
    } 
} 

return ret; 
관련 문제