Hoàn nguyên giao dịch Entity Framework 6


82

Với EF6, bạn có một giao dịch mới có thể được sử dụng như:

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();
                }
            }
        }

Có thực sự cần khôi phục khi mọi thứ đi ngang không? Tôi tò mò vì mô tả Cam kết có nội dung: "Cam kết giao dịch cơ bản tại cửa hàng".

Trong khi mô tả Rollback cho biết: "Quay lại giao dịch cơ bản của cửa hàng."

Điều này khiến tôi tò mò, bởi vì tôi thấy rằng nếu không gọi commit, các lệnh đã thực thi trước đó sẽ không được lưu trữ (điều này có vẻ hợp lý với tôi). Nhưng nếu đúng như vậy, thì lý do gì để gọi hàm Rollback? Trong EF5, tôi đã sử dụng TransactionScope không có chức năng Rollback (chỉ Hoàn thành), điều này có vẻ hợp lý với tôi. Vì lý do MS DTC, tôi không thể sử dụng TransactionScope nữa, nhưng tôi cũng không thể sử dụng thử bắt như ví dụ trên (tức là tôi chỉ cần Cam kết).


1
Bạn đã đọc kỹ về các giao dịch trong sql chưa? EF cố gắng bắt chước điều đó. AFAIK, nếu bạn không thực hiện một giao dịch trong sql, nó sẽ được khôi phục.
gunr2171

Cũng xem câu hỏi này .
gunr2171

Có, tôi biết về các giao dịch trong chính SQL. Tôi tò mò không biết EF làm gì, nhưng nếu họ bắt chước điều đó, thì điều đó có lý. Tôi sẽ xem nếu tôi có thể giải quyết nó. Cảm ơn bạn!
The Cookies Dog

SaveChanges () luôn xảy ra trong một giao dịch sẽ được khôi phục nếu có ngoại lệ xảy ra. Trong trường hợp của bạn, không cần phải cố gắng xử lý việc này theo cách thủ công (trong trường hợp cụ thể này, sẽ tốt hơn nếu thêm tất cả các thực thể trước và SaveChangeschỉ một lần).
Pawel

Tôi chỉ muốn các mục được lưu từ cả hai SaveChanges khi cả hai đều không bị lỗi, vì vậy có, tôi cần một giao dịch duy nhất xung quanh cả hai.
The Cookies Dog,

Câu trả lời:


117

Bạn không cần phải gọi Rollbacktheo cách thủ công vì bạn đang sử dụng usingcâu lệnh.

DbContextTransaction.Disposephương thức sẽ được gọi ở cuối usingkhối. Và nó sẽ tự động khôi phục giao dịch nếu giao dịch không được cam kết thành công (không được gọi hoặc gặp trường hợp ngoại lệ). Sau đây là mã nguồn của SqlInternalTransaction.Disposephương thức ( DbContextTransaction.Disposecuối cùng sẽ ủy quyền cho nó khi sử dụng trình cung cấp SqlServer):

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

Bạn thấy đấy, nó sẽ kiểm tra nếu _innerConnectionkhông phải là null, nếu không, hãy khôi phục giao dịch (nếu được cam kết, _innerConnectionsẽ là null). Hãy xem những gì Commithiện:

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);
    }
}

23

Miễn là bạn sẽ luôn sử dụng SQL Server với EF, không cần phải sử dụng rõ ràng hàm catch để gọi phương thức Rollback. Việc cho phép khối using tự động khôi phục trên mọi trường hợp ngoại lệ sẽ luôn hoạt động.

Tuy nhiên, khi bạn nghĩ về nó từ quan điểm Khung thực thể, bạn có thể thấy lý do tại sao tất cả các ví dụ đều sử dụng lệnh gọi khôi phục giao dịch một cách rõ ràng. Đối với EF, nhà cung cấp cơ sở dữ liệu là tùy ý và có thể cắm được và nhà cung cấp có thể được thay thế bằng MySQL hoặc bất kỳ cơ sở dữ liệu nào khác có triển khai nhà cung cấp EF. Do đó, theo quan điểm của EF, không có gì đảm bảo rằng nhà cung cấp sẽ tự động khôi phục giao dịch đã xử lý, vì EF không biết về việc triển khai của nhà cung cấp cơ sở dữ liệu.

Vì vậy, như một phương pháp hay nhất, tài liệu EF khuyên bạn nên Khôi phục một cách rõ ràng - đề phòng một ngày nào đó bạn thay đổi nhà cung cấp sang một triển khai không tự động khôi phục khi xử lý.

Theo ý kiến ​​của tôi, bất kỳ nhà cung cấp nào tốt và được viết tốt sẽ tự động khôi phục giao dịch trong quá trình định đoạt, vì vậy nỗ lực bổ sung để gói mọi thứ bên trong khối đang sử dụng bằng try-catch-rollback là quá cần thiết.


1
Cảm ơn cho cái nhìn sâu sắc này. Tôi đi sâu vào mã và bạn kết thúc với Dispose của lớp trừu tượng DbTransaction được ghi đè trong SqlTransaction mà bản thân nó gọi là SqlInternalTransaction mà Mouhong Lin đã đề cập.
ShawnFumo

4
  1. Vì bạn đã viết một khối 'using' để khởi tạo giao dịch, bạn không cần phải đề cập đến hàm Rollback một cách rõ ràng vì nó sẽ tự động được khôi phục (trừ khi nó được cam kết) tại thời điểm xử lý.
  2. Nhưng nếu bạn khởi tạo nó mà không sử dụng khối, thì trong trường hợp đó, điều cần thiết là phải khôi phục giao dịch trong trường hợp ngoại lệ (chính xác là trong một khối bắt) và điều đó cũng bằng kiểm tra rỗng để có mã mạnh hơn. Hoạt động của BeginTransaction không giống như phạm vi giao dịch (chỉ cần một chức năng hoàn chỉnh nếu tất cả các hoạt động được hoàn thành thành công). Thay vào đó, nó giống như hoạt động của các giao dịch Sql.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.