[c#] Entity Framework 6 트랜잭션 롤백

EF6를 사용하면 다음과 같이 사용할 수있는 새 트랜잭션이 있습니다.

using (var context = new PostEntityContainer())
        {
            using (var dbcxtransaction = context.Database.BeginTransaction())
            {
                try
                {
                    PostInformation NewPost = new PostInformation()
                    {
                        PostId = 101,
                        Content = "This is my first Post related to Entity Model",
                        Title = "Transaction in EF 6 beta"
                    };
                    context.Post_Details.Add(NewPost);
                    context.SaveChanges();
                    PostAdditionalInformation PostInformation = new PostAdditionalInformation()
                    {
                        PostId = (101),
                        PostName = "Working With Transaction in Entity Model 6 Beta Version"
                    };

                    context.PostAddtional_Details.Add(PostInformation);
                    context.SaveChanges();

                    dbcxtransaction.Commit();
                }
                catch
                {
                    dbcxtransaction.Rollback();
                }
            }
        }

일이 옆으로 갈 때 롤백이 실제로 필요합니까? Commit 설명에 “기본 저장소 트랜잭션을 커밋합니다.”라고 나와 있기 때문에 궁금합니다.

롤백 설명은 “기본 저장소 트랜잭션을 롤백합니다.”라고 말합니다.

이것은 Commit이 호출되지 않으면 이전에 실행 된 명령이 저장되지 않을 것으로 보이므로 궁금합니다. 하지만 그렇다면 Rollback 함수를 호출하는 이유는 무엇일까요? EF5에서는 논리적으로 보이는 롤백 기능 (완료 만 해당)이없는 TransactionScope를 사용했습니다. MS DTC 이유로 인해 TransactionScope를 더 이상 사용할 수 없지만 위의 예와 같은 try catch를 사용할 수 없습니다 (즉, Commit 만 필요함).



답변

문을 Rollback사용하고 있으므로 수동으로 호출 할 필요가 없습니다 using.

DbContextTransaction.Dispose메소드는 using블록 의 끝에서 호출됩니다 . 그리고 트랜잭션이 성공적으로 커밋되지 않은 경우 (호출되지 않거나 예외가 발생하면) 트랜잭션을 자동으로 롤백합니다. 다음은 SqlInternalTransaction.Dispose메서드 의 소스 코드입니다 ( DbContextTransaction.DisposeSqlServer 공급자를 사용할 때 마지막으로 위임 됨).

private void Dispose(bool disposing)
{
    // ...
    if (disposing && this._innerConnection != null)
    {
        this._disposing = true;
        this.Rollback();
    }
}

_innerConnectionnull이 아닌지 확인 하고 그렇지 않은 경우 트랜잭션을 롤백합니다 (커밋 된 경우 _innerConnectionnull이 됨). 무엇을하는지 보자 Commit:

internal void Commit()
{
    // Ignore many details here...

    this._innerConnection.ExecuteTransaction(...);

    if (!this.IsZombied && !this._innerConnection.IsYukonOrNewer)
    {
        // Zombie() method will set _innerConnection to null
        this.Zombie();
    }
    else
    {
        this.ZombieParent();
    }

    // Ignore many details here...
}

internal void Zombie()
{
    this.ZombieParent();

    SqlInternalConnection innerConnection = this._innerConnection;

    // Set the _innerConnection to null
    this._innerConnection = null;

    if (innerConnection != null)
    {
        innerConnection.DisconnectTransaction(this);
    }
}


답변

항상 EF와 함께 SQL Server를 사용하는 경우에는 catch를 명시 적으로 사용하여 Rollback 메서드를 호출 할 필요가 없습니다. using 블록이 모든 예외에 대해 자동으로 롤백하도록 허용하면 항상 작동합니다.

그러나 Entity Framework 관점에서 생각해 보면 모든 예제에서 명시 적 호출을 사용하여 트랜잭션을 롤백하는 이유를 알 수 있습니다. EF에게 데이터베이스 공급자는 임의적이고 플러그 가능하며 공급자는 MySQL 또는 EF 공급자 구현이있는 다른 데이터베이스로 대체 될 수 있습니다. 따라서 EF의 관점에서 볼 때 EF는 데이터베이스 공급자의 구현에 대해 알지 못하기 때문에 공급자가 삭제 된 트랜잭션을 자동으로 롤백 할 것이라는 보장이 없습니다.

따라서 모범 사례로 EF 문서에서는 언젠가 공급자를 폐기시 자동 롤백하지 않는 구현으로 변경하는 경우에 대비하여 명시 적으로 롤백 할 것을 권장합니다.

제 생각에는 훌륭하고 잘 작성된 공급자는 처리에서 트랜잭션을 자동으로 롤백하므로 try-catch-rollback을 사용하여 using 블록 내부의 모든 것을 래핑하려는 추가 노력이 과도합니다.


답변

  1. 트랜잭션을 인스턴스화하기 위해 ‘using’블록을 작성 했으므로 롤백 함수는 처리시 자동으로 롤백되므로 명시 적으로 언급 할 필요가 없습니다 (커밋되지 않은 경우).
  2. 그러나 using 블록없이 인스턴스화하는 경우 예외 (정확히 catch 블록에서)의 경우 트랜잭션을 롤백하고 더 강력한 코드를위한 null 검사를 사용하여 트랜잭션을 롤백해야합니다. BeginTransaction의 작동은 트랜잭션 범위와 다릅니다 (모든 작업이 성공적으로 완료된 경우 완전한 기능 만 필요함). 대신 SQL 트랜잭션의 작동과 유사합니다.


답변