트랜잭션 또는 SaveChanges (false) 및 AcceptAllChanges ()를 사용합니까?


346

나는 거래를 조사해 왔으며, 내가 통과 false하고 오류가없는 SaveChanges()한 전화 AcceptAllChanges()하는 한 EF에서 스스로 돌보는 것처럼 보입니다 .

SaveChanges(false);
// ...
AcceptAllChanges();

문제가 발생하면 어떻게합니까? 롤백하거나 내 방법이 범위를 벗어나 자마자 거래가 종료됩니까?

트랜잭션 중간에 할당 된 들여 쓰기 열은 어떻게됩니까? 광산이 나 빠지기 전에 다른 사람이 기록을 추가하면 ID 값이 누락 될 것입니다.

TransactionScope내 코드에서 표준 클래스 를 사용해야 할 이유가 있습니까?


1
이것은SaveChanges(fase); ... AcceptAllChanges();패턴이 처음 인지 이해하는 데 도움 되었습니다 . 위의 질문에 대한 대답이 블로그 작성자에 의해 어떻게 작성되었으며 그 질문이 다른 질문에서 참조 되는지 확인하십시오 . 모두 함께 제공됩니다.
붉은 완두콩

답변:


451

Entity Framework를 사용하면 대부분 SaveChanges()충분합니다. 그러면 트랜잭션이 생성되거나 주변 트랜잭션에 참여하고 해당 트랜잭션에 필요한 모든 작업이 수행됩니다.

때로는 SaveChanges(false) + AcceptAllChanges()페어링이 유용 하지만 .

가장 유용한 장소는 두 개의 다른 컨텍스트에서 분산 트랜잭션을 수행하려는 경우입니다.

즉 다음과 같은 것 (나쁜) :

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save and discard changes
    context1.SaveChanges();

    //Save and discard changes
    context2.SaveChanges();

    //if we get here things are looking good.
    scope.Complete();
}

경우 context1.SaveChanges()성공하지만 context2.SaveChanges()전체 분산 트랜잭션이 중단됩니다 실패합니다. 그러나 불행히도 Entity Framework는의 변경 사항을 이미 삭제 context1했기 때문에 실패를 재생하거나 효과적으로 기록 할 수 없습니다.

그러나 코드를 다음과 같이 변경하면

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save Changes but don't discard yet
    context1.SaveChanges(false);

    //Save Changes but don't discard yet
    context2.SaveChanges(false);

    //if we get here things are looking good.
    scope.Complete();
    context1.AcceptAllChanges();
    context2.AcceptAllChanges();

}

호출 SaveChanges(false)이 필요한 명령을 데이터베이스 에 전송하는 동안 컨텍스트 자체는 변경되지 않으므로 필요한 경우 다시 수행하거나 ObjectStateManager원하는 경우 질문 할 수 있습니다 .

즉, 트랜잭션이 실제로 예외를 발생시키는 경우 각 컨텍스트의 상태를 다시 시도하거나 로깅하여 ObjectStateManager어딘가에 보상 할 수 있습니다 .

자세한 내용은 블로그 게시물 을 참조하십시오 .


3
고마워, 고마워 ... 그래서 뭔가 실패하면 롤백 할 필요가 없습니까 ?? SaveChanges, 저장 대상으로 표시하지만, 모든 변경을 수락 할 때까지 실제로 커밋하지는 않습니다. 그러나 무언가 잘못 될 경우. 롤백 할 필요가 없어서 객체가 올바른 상태로 돌아 갑니까?
마크 스미스

33
@Mark : "롤백 (roll-back)"이라는 의미로 객체를 데이터베이스에있는 상태로 되 돌리면 아니요, 그렇지 않으면 객체에 대한 모든 사용자의 변경 사항을 잃기 때문에 그렇게하고 싶지 않습니다. . EF에 "실제로 저장되었으므로 저장해야 할 항목을 잊어 버릴 수 있습니다."라고 SaveChanges(false)데이터베이스에 실제로 업데이트합니다 AcceptAllChanges(). 경우 SaveChanges(false)실패 AcceptAllChanges()호출되지 않을 것이며, EF는 여전히 변경된와 필요가 데이터베이스에 다시 저장하는 특성을 가진 것으로 개체를 고려하게됩니다.
BlueRaja-대니 Pflughoeft

Code First를 사용하여이 작업을 수행하는 방법을 조언 해 줄 수 있습니까? SaveChanges를 또는 AcceptAllChanges 방법에는 매개 변수가 없습니다
키얼 스틴 탐욕

2
나는 코드 첫 번째로이 기술을 사용하는 방법에 대한 질문을했습니다 여기
키얼 스틴 탐욕을

13
EF 6.1에서는 더 이상 사용할 수 없습니다. 현재 어떤 종류의 조정을해야하는지 알고 있습니까?
Alex Dresko

113

EF6 (Entity Framework 6+)을 사용하는 경우 데이터베이스 호출이 SQL로 변경되었습니다.
참조 : http://msdn.microsoft.com/en-us/data/dn456843.aspx

context.Database.BeginTransaction을 사용하십시오.

MSDN에서 :

using (var context = new BloggingContext()) 
{ 
    using (var dbContextTransaction = context.Database.BeginTransaction()) 
    { 
        try 
        { 
            context.Database.ExecuteSqlCommand( 
                @"UPDATE Blogs SET Rating = 5" + 
                    " WHERE Name LIKE '%Entity Framework%'" 
                ); 

            var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
            foreach (var post in query) 
            { 
                post.Title += "[Cool Blog]"; 
            } 

            context.SaveChanges(); 

            dbContextTransaction.Commit(); 
        } 
        catch (Exception) 
        { 
            dbContextTransaction.Rollback(); //Required according to MSDN article 
            throw; //Not in MSDN article, but recommended so the exception still bubbles up
        } 
    } 
} 

52
트랜잭션에서 "사용"을 사용할 때 roolback을 사용한 try-catch는 필요하지 않습니다.
Robert

12
이와 같은 예외를 포착하는 데 예외가 있습니다. 데이터베이스 작업이 자동으로 실패합니다. SO의 특성상 누군가가이 예제를 작성하여 프로덕션 애플리케이션에서 사용할 수 있습니다.
B2K

3
@ B2K : 좋은 지적이지만이 코드는 연결된 Microsoft 기사 에서 복사되었습니다 . 나는 아무도 사용하지 희망 그들의 :) 생산에 코드를
J 브라이언 가격

6
@Robert MSDN 기사에 따르면 Rollback ()이 필요합니다. TransactionScope 예제에 대한 롤백 명령을 의도적으로 생략합니다. @ B2K throw;MSDN 스 니펫에 추가했으며 MSDN 기사의 원본이 아님을 분명히 나타 냈습니다 .
Todd

6
(정확한 경우)이 문제를 해결할 수 있습니다. EF + MSSQL과 같은 사운드는 롤백이 필요하지 않지만 EF + 다른 SQL 공급자는 필요합니다. EF는 어떤 데이터베이스와 대화 Rollback()하고 있는지 알 수 없으므로 MySql과 자동으로 대화하지 않는 경우에 호출됩니다.
Jared와 같은 단어

-5

일부 데이터베이스는 dbContextTransaction.Commit ()에서 예외를 발생시킬 수 있으므로 다음과 같이하십시오.

using (var context = new BloggingContext()) 
{ 
  using (var dbContextTransaction = context.Database.BeginTransaction()) 
  { 
    try 
    { 
      context.Database.ExecuteSqlCommand( 
          @"UPDATE Blogs SET Rating = 5" + 
              " WHERE Name LIKE '%Entity Framework%'" 
          ); 

      var query = context.Posts.Where(p => p.Blog.Rating >= 5); 
      foreach (var post in query) 
      { 
          post.Title += "[Cool Blog]"; 
      } 

      context.SaveChanges(false); 

      dbContextTransaction.Commit(); 

      context.AcceptAllChanges();
    } 
    catch (Exception) 
    { 
      dbContextTransaction.Rollback(); 
    } 
  } 
} 

7
이와 같은 예외를 포착하는 데 예외가 있습니다. 데이터베이스 작업이 자동으로 실패합니다. SO의 특성상 누군가가이 예제를 작성하여 프로덕션 애플리케이션에서 사용할 수 있습니다.
B2K

6
이것이 인용 한 MSDN 페이지에 기여를 한이 다른 대답 과 본질적으로 동일하지 않습니까? 내가 볼 수있는 유일한 차이점은 합격이다 로 하고, 추가로 호출 . falsecontext.SaveChanges();context.AcceptAllChanges();
Wai Ha Lee

@ B2K 롤백이 필요하지 않습니다-트랜잭션이 작동하지 않으면 아무것도 커밋되지 않습니다. 또한 롤백에 대한 명시적인 호출이 실패 할 수 있습니다. 여기에서 내 대답을보십시오 stackoverflow.com/questions/41385740/…
Ken

롤백은 내가 반대하는 것이 아닙니다. 이 답변의 작성자는 예외를 다시 발생시키기 위해 코드를 업데이트하여 반대 의견을 해결했습니다.
B2K

죄송합니다. 전화로 댓글을 달았습니다. Todd는 예외를 다시 발생시킵니다. eMeL은 예외입니다. 캐치에는 롤백을 유발하는 문제점을 개발자 또는 사용자에게 알리는 무언가가 있어야합니다. 로그 파일에 쓰거나 예외를 다시 발생 시키거나 사용자에게 메시지를 반환 할 수 있습니다.
B2K
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.