2010-09-03 62 views
9

Tuyên bố từ chối trách nhiệm: Tôi biết IDisposable nên được triển khai khi xử lý các tài nguyên không được quản lý. Phần còn lại của mã phải được xác định và làm using (...) { } (tương đương với try {} finally { Dispose(); }) để đảm bảo việc dọn dẹp càng sớm càng tốt. Ngoài ra, GC sẽ not call Dispose(), do đó, mẫu được đề xuất là ghi đè phương thức Finalize() (trong C# bằng cú pháp destructor), sau đó gọi Dispose(). GC thường sẽ gọi số Finalize() (trừ khi GC.SuppressFinalize() đã được gọi).SqlConnection sẽ bị xử lý bởi GC?

Vấn đề: Vì vậy, bây giờ mà tôi đã nhận rằng trên đường đi, tôi có một kịch bản kỳ lạ mà tôi không thể làm using (SqlConnection...) { } do mã ngoài tầm kiểm soát của tôi. Tôi thường có thể làm một định nghĩa Dispose(), nhưng không thể đảm bảo nó. Tôi đã sử dụng Reflector để tháo rời SqlConnection và thấy rằng nó sử dụng Dispose(), nhưng trừ khi tôi bị mù không có finalizer/destructor (Finalize() hoặc ~SqlConnection()). Điều đó có nghĩa là GC sẽ không "dọn dẹp" (gửi lại hồ bơi) kết nối trong trường hợp kỳ lạ tôi không thể? Tôi đã không thể tìm thấy bất kỳ điều gì dứt khoát ...

Trả lời

7

Vâng, nó sẽ không được xử lý, vì quyết toán không được xử lý.

Có một finaliser trong System.ComponentModel.Component, nhưng bị chặn trong hàm tạo của SQLConnection. Đây là một ý kiến ​​hay nếu bạn kế thừa từ một cái gì đó với một finaliser mà bạn biết với 100% chắc chắn bạn sẽ không cần, nhưng một ý tưởng tồi khác. Trong trường hợp này, đó là một ý tưởng hay.

Hãy nhớ rằng, SqlConnection là trình bao bọc trên kết nối "thực". Thật vậy, nó rất có thể là một wrapper trên một bộ thay đổi của các đối tượng đại diện cho các trạng thái kết nối khác nhau. Đây là một phần của cơ chế cho phép kết nối "thực" được gộp lại hiệu quả, mỗi lần bạn gọi Open() nó lấy đối tượng liên quan từ hồ bơi và mỗi lần bạn gọi Close() (dù trực tiếp, bằng Dispose() hoặc bằng cách thoát khỏi phạm vi của một using) nó trả về nó.

Bây giờ, hãy nhớ rằng chỉ có các đối tượng trực tiếp nắm giữ một nguồn tài nguyên không được quản lý hoặc một cái gì đó khác không phải là mối quan tâm của GC, cần phải được hoàn thành. SqlConnection giữ một đối tượng có thể (tùy thuộc vào trạng thái SqlConnection) là một đối tượng có tài nguyên không được quản lý (hoặc thực sự, sâu hơn thông qua tổ của các lớp). Do đó, không cần phải tự hoàn thành SqlConnection. Hãy xem xét ba cách có thể mở SqlConnection có thể ngừng là một số mở là SqlConnection:

  1. Close() được gọi. Điều này ngay lập tức trả về kết nối thực sự với pool (hoặc đóng nó nếu không có pooling).
  2. Dispose() được gọi. Điều này gọi Close() với cùng tác dụng.
  3. Đối tượng bị thu gom rác.

Bây giờ, trong trường hợp thứ ba, đối tượng có tham chiếu đến đối tượng có kết nối thực. Nó cũng là đối tượng duy nhất làm như vậy. Đối tượng đó là do đó cũng sẽ được thu gom rác thải. Nếu nó có một finaliser (mà nó có thể làm, mặc dù tôi sẽ không giả định không có thêm thủ đoạn thông minh đang diễn ra) sau đó finaliser sẽ gây ra nó được đặt vào hàng đợi finaliser, và nó sẽ được cuối cùng hoàn thành.

Nếu SqlConnection đã có một finaliser, những tác động thực sự duy nhất sẽ là:

  1. tiềm năng cho mã lỗi (đối phó với các thành viên finalisable trong mã finaliser là đầy, như bạn không biết hay không mà họ đã đã được hoàn thành).
  2. Tiềm năng làm chậm mọi thứ (kết nối thực sự sẽ được hoàn tất, tốt nhất là chúng tôi chỉ làm chậm quá trình hoàn thành và GC).
  3. Không có gì để làm ở đây anyway (kết nối thực sự sẽ được hoàn thành mà không cần bất kỳ trợ giúp nào ở đây).

Vì vậy, việc đặt cược cuối cùng trên SqlConnection là thua cuộc mà không giành chiến thắng. Ngoài ra, kết nối thực sự của bạn hy vọng cuối cùng sẽ được hoàn thành.

Điều này cho biết, nó vẫn còn xa lý tưởng và vẫn còn rất có khả năng bị rò rỉ kết nối. Bạn có thể nêu chi tiết chính xác lý do tại sao bạn không thể gọi số Close() hoặc tự hủy bỏ? Mã có thể quản lý kết nối không gọi gần cho bạn (đối tượng sẽ kết thúc ngày của nó ở đâu đó và phải đóng cửa ở đó) không?

Bạn có cần giữ nó sống cho một số IDataReader hoặc một đối tượng có nguồn cấp dữ liệu từ IDataReader để được phép hoàn tất không? Trong trường hợp nào, bạn có thể sử dụng cờ CommandBehavior.CloseConnection để đóng (hoặc xử lý) người đọc đóng kết nối không? Trường hợp thứ hai này là về trường hợp duy nhất mà tôi có thể nhớ lại bao giờ phải để một kết nối rời khỏi phạm vi không được xử lý.

+0

"SqlConnection giữ một đối tượng có thể (tùy thuộc vào trạng thái của SqlConnection) là một đối tượng chứa tài nguyên không được quản lý" - điểm tốt, tôi đã không xác minh rằng SqlConnection thực sự chứa tài nguyên không được quản lý. Về cơ bản, mã quản lý kết nối là một thư viện bên ngoài không được triển khai đúng cách. Tôi nghĩ rằng tôi sẽ có thể thay đổi nó sau khi tất cả và tránh vấn đề này hoàn toàn. –

+0

Rất vui khi biết điều đó. Vấn đề hoàn thành được thực hiện trên đúng đối tượng là tốt và tốt, nhưng sẽ không giúp bạn nếu quyết toán không xảy ra thường xuyên (và IME nó hầu như không bao giờ - hoặc tệ hơn, tệ hơn nhiều, nó thực hiện trong thử nghiệm chứ không phải trực tiếp). –

+0

+1 câu trả lời hay. Im một chút nhầm lẫn về tuyên bố sau trên msdn cho thấy rằng SqlConnection là một tài nguyên quản lý. "Không gọi Close hoặc Dispose trên một kết nối, một DataReader, hoặc bất kỳ đối tượng ** quản lý ** khác trong phương thức Finalize của lớp của bạn." từ [HERE] (http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.close%28v=vs.110%29.aspx) –