2010-05-27 30 views
63

Tôi có một khối mã chạy bên trong một TransactionScope và trong khối mã này, tôi thực hiện một số cuộc gọi đến DB. Chọn, cập nhật, tạo và xóa, toàn bộ gam màu. Khi tôi thực hiện xóa của mình, tôi thực hiện nó bằng cách sử dụng một phương thức mở rộng của SqlCommand sẽ tự động gửi lại truy vấn nếu nó bị khóa khi truy vấn này có khả năng có thể gây ra bế tắc.TransactionScope Hoàn thành sớm

Tôi tin rằng sự cố xảy ra khi bế tắc bị nhấn và hàm sẽ cố gắng gửi lại truy vấn. Đây là lỗi tôi nhận được:

Giao dịch liên kết với kết nối hiện tại đã hoàn tất nhưng chưa được xử lý. Giao dịch phải được xử lý trước khi kết nối có thể được sử dụng để thực hiện các câu lệnh SQL.

Đây là mã đơn giản mà thực hiện truy vấn (tất cả các mã dưới đây thực hiện trong phạm vi sử dụng của TransactionScope):

using (sqlCommand.Connection = new SqlConnection(ConnectionStrings.App)) 
{ 
    sqlCommand.Connection.Open(); 
    sqlCommand.ExecuteNonQueryWithDeadlockHandling(); 
} 

Dưới đây là phương pháp mở rộng mà gửi lại biểu truy vấn bế tắc:

public static class SqlCommandExtender 
{ 
    private const int DEADLOCK_ERROR = 1205; 
    private const int MAXIMUM_DEADLOCK_RETRIES = 5; 
    private const int SLEEP_INCREMENT = 100; 

    public static void ExecuteNonQueryWithDeadlockHandling(this SqlCommand sqlCommand) 
    { 
     int count = 0; 
     SqlException deadlockException = null; 

     do 
     { 
      if (count > 0) Thread.Sleep(count * SLEEP_INCREMENT); 
      deadlockException = ExecuteNonQuery(sqlCommand); 
      count++; 
     } 
     while (deadlockException != null && count < MAXIMUM_DEADLOCK_RETRIES); 

     if (deadlockException != null) throw deadlockException; 
    } 

    private static SqlException ExecuteNonQuery(SqlCommand sqlCommand) 
    { 
     try 
     { 
      sqlCommand.ExecuteNonQuery(); 
     } 
     catch (SqlException exception) 
     { 
      if (exception.Number == DEADLOCK_ERROR) return exception; 
      throw; 
     } 

     return null; 
    } 
} 

lỗi này xảy ra trên dòng:

sqlCommand.ExecuteNonQuery(); 

Trả lời

59

Đừng quên để ngăn chặn các câu lệnh chọn của bạn từ TransactionScope. Trong SQL Server 2005 trở lên, ngay cả khi bạn sử dụng cùng với (nolock), các khóa vẫn được tạo trên các bảng chọn chạm. Kiểm tra này, nó cho bạn thấy how to setup and use TransactionScope.

using(TransactionScope ts = new TransactionScope 
{ 
    // db calls here are in the transaction 
    using(TransactionScope tsSuppressed = new TransactionScope (TransactionScopeOption.Suppress)) 
    { 
    // all db calls here are now not in the transaction 
    } 
} 
+1

Cảm ơn bạn đã bỏ qua các giao dịch ngăn chặn trên các câu lệnh chọn. Điều đó đã giúp giải quyết vấn đề thời gian chờ khiến tôi phát điên. –

+2

Câu trả lời tuyệt vời. Điều đó đã khiến tôi phát điên lên một bộ sưu tập tuyển chọn/chèn bộ sưu tập các lệnh sql. Thêm tùy chọn Suppress tự động giải quyết vấn đề. – IoChaos

+1

Holy crap cảm ơn bạn! Tôi đã vật lộn với điều này cả ngày. Một giải pháp đơn giản như vậy. – rossipedia

10

Nếu ngoại lệ xảy ra bên trong một TransactionScope, nó sẽ được khôi phục. Điều này có nghĩa là TransactionScope được thực hiện. Bây giờ bạn phải gọi dispose() trên đó và bắt đầu một giao dịch mới. Tôi thành thật không chắc chắn nếu bạn có thể tái sử dụng cũ TransactionScope hay không, tôi đã không bao giờ cố gắng, nhưng tôi sẽ giả sử không.

+2

Ngay cả khi ngoại lệ bị bắt, giao dịch được khôi phục? – Chris

+0

Tôi chưa bao giờ thử nghiệm với nó như đối với tôi, exception = error = stop và rollback. Tuy nhiên, nó có vẻ như vậy từ những gì bạn mô tả. – Donnie

+1

Đây là sai, và không phải là những gì xảy ra trên trường hợp ngoại lệ. Bạn cũng không cần phải gọi Dispose() sau đó. Khi TransactionScope được tạo ra trong một câu lệnh using, câu lệnh using sẽ Dispose() là TransactionScope on Exceptions. – thewhiteambit

20

Tôi có thể tạo lại sự cố. Đó là thời gian chờ giao dịch.

using (new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 0, 0, 1))) 
{ 
    using (SqlConnection connection = new SqlConnection(connectionString)) 
    { 
     connection.Open(); 
     using (var sqlCommand = connection.CreateCommand()) 
     { 
      for (int i = 0; i < 10000; i++) 
      { 
       sqlCommand.CommandText = "select * from actor"; 
       using (var sqlDataReader = sqlCommand.ExecuteReader()) 
       { 
        while (sqlDataReader.Read()) 
        { 
        } 
       } 
      } 
     } 
    } 
} 

Ném System.InvalidOperationException với tin nhắn này: "Các giao dịch liên quan đến việc kết nối hiện đã hoàn thành nhưng chưa được xử lý giao dịch phải được xử lý trước khi kết nối có thể được sử dụng để thực thi câu lệnh SQL.."

Để giải quyết vấn đề, bạn truy vấn chạy nhanh hơn hoặc không đúng thời gian chờ.

6

Vấn đề của tôi là một vấn đề ngu ngốc, nếu bạn ngồi trên một lỗi gỡ lỗi thông qua thời gian chờ bạn sẽ nhận được điều này. Mặt Palm

Man, lập trình làm cho bạn cảm thấy dày vài ngày ...

39

tôi đã tìm thấy rằng thông điệp này có thể xảy ra khi một giao dịch chạy trong một thời gian dài hơn maxTimeout cho System.Transactions. Không quan trọng là số lượng TransactionOptions.Timeout được tăng lên, nó không thể vượt quá maxTimeout.

Giá trị mặc định của maxTimeout được thiết lập để 10 phút và giá trị của nó có thể chỉ được sửa đổi trong machine.config

Thêm dòng sau (ở mức cấu hình) đến machine.config để thay đổi thời gian chờ:

<configuration> 
    <system.transactions> 
     <machineSettings maxTimeout="00:30:00" /> 
    </system.transactions> 
</configuration> 

Các Machine.config thể được tìm thấy tại địa chỉ: %windir%\Microsoft.NET\Framework\[version]\config\machine.config

Bạn có thể đọc thêm về nó trong bài viết trên blog này: http://thecodesaysitall.blogspot.se/2012/04/long-running-systemtransactions.html

+3

Hãy nhớ rằng tệp cấu hình phân biệt chữ hoa chữ thường nên nó phải là "machineSettings" và "maxTimeout". Quá xấu, bạn không thể ghi đè lên điều này trong tệp app.config: ( –

+1

Cũng lưu ý rằng bạn * phải * đặt ở đầu * * của phần cấu hình, nếu không bạn sẽ gặp lỗi. –

6

Xác nhận lỗi này cũng có thể do thời gian chờ giao dịch gây ra. Chỉ cần thêm vào những gì Marcus + Rolf đã tuyên bố, nếu bạn không rõ ràng đặt thời gian chờ trên TransactionScope, TimeSpan hết thời gian chờ sẽ giả định giá trị mặc định. Giá trị mặc định là nhỏ của:

  1. Nếu bạn đã ghi đè địa phương app.config/web.config cài đặt, ví dụ

    <system.transactions> 
    <defaultSettings timeout="00:05:00" /> 
    </system.transactions> 
    
  2. Nhưng điều này sau đó được lấp đầy 'ở machine.config thiết <machineSettings maxTimeout="00:10:00" />

+0

Tôi có thể _override_ điều này trong * ** tệp app.config ***? – Kiquenet

1

ngoại lệ này cũng có thể được gây ra bởi disableMicrosoft Distributed Transaction Coordinator.

Nếu chúng ta muốn kích hoạt nó, chúng tôi chạy "dcomcnfg" và chọn "Component Services" -> "My Computer" -> "Distributed Transaction Coordinator" -> "Local Service DTC" và chọn "Thuộc tính".

Nó nên được kiểm tra "Cho phép từ xa khách hàng", "phép Inbound", "phép Outbound" và "No Authentication Required".

+0

Bạn có thể bật/tắt tính năng *** _programmatically_ bằng cách sử dụng 'BAT' hoặc' ps1' không? – Kiquenet