Sử dụng Giao dịch hoặc SaveChanges (sai) và AcceptAllChanges ()?


346

Tôi đã điều tra các giao dịch và có vẻ như họ tự chăm sóc bản thân trong EF miễn là tôi chuyển falseđến SaveChanges()và sau đó gọi AcceptAllChanges()nếu không có lỗi:

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

Điều gì xảy ra nếu một cái gì đó xấu đi? Tôi không phải quay lại hoặc ngay khi phương thức của tôi vượt quá phạm vi, giao dịch đã kết thúc chưa?

Điều gì xảy ra với bất kỳ cột indentiy nào được chỉ định nửa chừng trong giao dịch? Tôi đoán nếu ai đó đã thêm một bản ghi sau của tôi trước khi tôi bị hỏng thì điều này có nghĩa là sẽ có một giá trị Danh tính bị thiếu.

Có bất kỳ lý do để sử dụng TransactionScopelớp tiêu chuẩn trong mã của tôi?


1
Điều này giúp tôi hiểu tại sao SaveChanges(fase); ... AcceptAllChanges();là một mô hình ở nơi đầu tiên. Lưu ý cách câu trả lời được chấp nhận cho câu hỏi trên, được viết bởi tác giả của một blog - và blog đó được tham chiếu trong câu hỏi khác. Tất cả đến với nhau.
Hạt đậu đỏ

Câu trả lời:


451

Với Entity Framework hầu hết thời gian SaveChanges()là đủ. Điều này tạo ra một giao dịch, hoặc tham gia vào bất kỳ giao dịch xung quanh nào và thực hiện tất cả các công việc cần thiết trong giao dịch đó.

Đôi khi mặc dù việc SaveChanges(false) + AcceptAllChanges()ghép đôi là hữu ích.

Vị trí hữu ích nhất cho việc này là trong các tình huống bạn muốn thực hiện giao dịch phân tán trên hai bối cảnh khác nhau.

Tức là một cái gì đó như thế này (xấu):

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

Nếu context1.SaveChanges()thành công nhưng context2.SaveChanges()thất bại, toàn bộ giao dịch phân phối bị hủy bỏ. Nhưng thật không may, Entity Framework đã loại bỏ các thay đổi trên context1, vì vậy bạn không thể phát lại hoặc ghi lại sự thất bại một cách hiệu quả.

Nhưng nếu bạn thay đổi mã của mình thành như thế này:

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

}

Trong khi lệnh gọi SaveChanges(false)gửi các lệnh cần thiết đến cơ sở dữ liệu, bối cảnh không bị thay đổi, vì vậy bạn có thể thực hiện lại nếu cần hoặc bạn có thể thẩm vấn ObjectStateManagernếu muốn.

Điều này có nghĩa là nếu giao dịch thực sự ném một ngoại lệ bạn có thể bù, bằng cách thử lại hoặc trạng thái ghi nhật ký của từng bối cảnh ObjectStateManagerở đâu đó.

Xem bài viết trên blog của tôi để biết thêm.


3
Thật tuyệt, cảm ơn ... Vì vậy, nếu có gì đó không thành công tôi phải quay lại ?? SaveChanges, đánh dấu nó đã được lưu, nhưng thực tế không cam kết cho đến khi tôi chấp nhận trao đổi .. nhưng nếu có sự cố xảy ra .. tôi sẽ cần phải quay lại để tôi không trở về trạng thái chính xác?
đánh dấu smith

33
@Mark: nếu bạn "quay ngược" ý bạn, hãy hoàn nguyên các đối tượng của bạn về trạng thái mà chúng có trong cơ sở dữ liệu, thì không, bạn sẽ không muốn làm điều đó vì bạn sẽ mất tất cả các thay đổi của người dùng đối với các đối tượng . SaveChanges(false)thực tế việc cập nhật vào cơ sở dữ liệu, trong khi AcceptAllChanges()nói với EF, "Được rồi, bạn có thể quên những thứ cần lưu, bởi vì chúng đã được lưu thành công." Nếu SaveChanges(false)thất bại, AcceptAllChanges()sẽ không bao giờ được gọi và EF vẫn coi đối tượng của bạn là có các thuộc tính đã được thay đổi và cần được lưu lại vào cơ sở dữ liệu.
BlueRaja - Daniel Pflughoeft

Bạn có thể tư vấn làm thế nào để làm điều này bằng cách sử dụng Code First? Không có tham số nào cho phương thức SaveChanges hoặc AcceptAllChanges
Kirsten Greed

2
Tôi đã hỏi một câu hỏi về việc sử dụng kỹ thuật này với Code First tại đây
Kirsten Greed

13
Điều này không còn có thể có trong EF 6.1. Bạn có biết những loại điều chỉnh cần phải được thực hiện để làm việc bây giờ?
Alexoupko

113

Nếu bạn đang sử dụng EF6 (Entity Framework 6+), điều này đã thay đổi đối với các cuộc gọi cơ sở dữ liệu sang SQL.
Xem: http://msdn.microsoft.com/en-us/data/dn456843.aspx

sử dụng bối cảnh.Database.BeginTransaction.

Từ 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
        } 
    } 
} 

51
không cần thử với roolback khi bạn đang sử dụng "sử dụng" trên giao dịch.
Robert

12
Tôi lấy một ngoại lệ để bẫy ngoại lệ như thế này. Nó làm cho hoạt động cơ sở dữ liệu thất bại âm thầm. Do bản chất của SO, ai đó có thể lấy ví dụ này và sử dụng nó trong một ứng dụng sản xuất.
B2K

3
@ B2K: Điểm tốt, nhưng mã này được sao chép từ bài viết được liên kết của Microsoft. Tôi hy vọng không ai sử dụng mã của họ trong sản xuất :)
J Bryan Giá

6
@Robert Theo bài viết MSDN Rollback () là cần thiết. Họ cố tình bỏ qua một lệnh Rollback cho ví dụ TransactionScope. @ B2K Tôi đã thêm vào throw;đoạn mã MSDN và chỉ rõ rằng đó không phải là bản gốc từ bài viết MSDN.
Todd

6
(Nếu đúng) Điều này có thể làm sáng tỏ mọi thứ: Âm thanh như EF + MSSQL không cần Rollback, nhưng các nhà cung cấp SQL khác của EF + có thể. Vì EF được cho là không biết về cơ sở dữ liệu mà nó đang nói đến, nên Rollback()được gọi trong trường hợp nó đang nói chuyện với MySql hoặc thứ gì đó không có hành vi tự động đó.
Những từ như Jared

-5

Bởi vì một số cơ sở dữ liệu có thể đưa ra một ngoại lệ tại dbContextTransaction.Commit () nên điều này tốt hơn:

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
Tôi lấy một ngoại lệ để bẫy ngoại lệ như thế này. Nó làm cho hoạt động cơ sở dữ liệu thất bại âm thầm. Do bản chất của SO, ai đó có thể lấy ví dụ này và sử dụng nó trong một ứng dụng sản xuất.
B2K

6
Đây không phải là về cơ bản giống như câu trả lời khác mà đã quy kết cho trang MSDN mà nó trích dẫn? Sự khác biệt duy nhất tôi thấy là bạn chuyển falsevào context.SaveChanges();và gọi thêm context.AcceptAllChanges();.
Wai Ha Lee

@ B2K không yêu cầu rollback - nếu giao dịch không hoạt động thì không có gì được cam kết. Ngoài ra, cuộc gọi rõ ràng tới Rollback có thể thất bại - xem câu trả lời của tôi ở đây stackoverflow.com/questions/41385740/ Kẻ
Ken

Rollback không phải là những gì tôi phản đối. Tác giả của câu trả lời này đã cập nhật mã của họ để suy nghĩ lại ngoại lệ, do đó giải quyết những gì tôi đang phản đối.
B2K

Xin lỗi, tôi nhận xét từ điện thoại của tôi. Todd lại ném ngoại lệ, eMeL thì không. Cần có một cái gì đó trong phần khai thác để thông báo cho nhà phát triển hoặc người dùng của sự cố gây ra sự quay trở lại. Đó có thể là ghi vào tệp nhật ký, ghi lại ngoại lệ hoặc trả lại tin nhắn cho người dùng.
B2K
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.