5

Tôi đã kế thừa một khung web theo đó nhà phát triển trước đó đã mở và đóng các kết nối cơ sở dữ liệu của anh ấy bằng các phương thức init/unload của vòng đời trang. Cơ bản constructor là như thế này (đơn giản hóa để chứng minh điểm);Đóng một conneciton trong phương thức "unload"

public class BasePage 
{ 
    protected DBConnection _conn; 

    public BasePage() 
    { 
     Init += StartConnection; 
     Unload += EndConnection; 
    } 

    private void StartConnection(object sender, EventArgs e) 
    { 
     _conn = new DBConnection(Application["connectionstring"].ToString()); 
    } 

    private void EndConnection(object sender, EventArgs e) 
    { 
     if (_conn == null) 
     return; 

     if (_conn.Connection.State == ConnectionState.Open) 
     { 
    _conn.Close(); 
     _conn.Dispose(); 
     } 
    } 
} 

Phát triển khá nhanh vì tôi đã đến đây nên tôi chưa bao giờ dừng lại để xem xét. Gần đây, các lượt truy cập đã được cập nhật và chúng tôi đã bắt đầu nhận được thời gian chờ "Thời gian chờ đã hết hạn. Thời gian chờ đã trôi qua trước khi có kết nối từ hồ bơi ...".

Tôi hiện đang trải qua phần còn lại của mã tìm kiếm các rò rỉ kết nối có thể, nhưng đoạn mã trên chưa bao giờ hoàn toàn phù hợp với tôi và tôi muốn loại bỏ nó như là một thủ phạm tiềm năng. Về câu hỏi đó;

Tôi có thể dựa vào phương thức "dỡ" luôn luôn được gọi, ngay cả trong trường hợp ngoại lệ? Hoặc bất cứ ai có thể nhìn thấy bất kỳ vấn đề tiềm năng khác bằng cách sử dụng các mô hình trên mà sẽ làm cho nó một nghi can chính cho những rò rỉ kết nối?

Chúc mừng,

Mikey

EDIT: Trong gỡ lỗi, các phương pháp dỡ bỏ luôn được gọi là thậm chí nếu có một ngoại lệ. Tôi thực sự chỉ cần biết về bất kỳ kịch bản mà phương pháp này sẽ không được gọi là vì vậy tôi có thể tìm ra nếu đây là bit tôi cần phải được refactoring đầu tiên.

CHỈNH SỬA: Cảm ơn những người đã phản hồi cho đến giờ nhưng xin vui lòng không đề xuất thêm về lớp IDisposable hoặc mẫu "sử dụng" hoặc "bắt/cuối cùng" - Đây không phải là câu hỏi của tôi! Câu hỏi của tôi là cụ thể cho dù một trang có thể chạy sự kiện "Init" của nó nhưng sau đó không chạy là "Unload" sự kiện, và tại sao điều này có thể xảy ra.

Trả lời

2

Tôi không có kiến ​​thức dứt khoát về việc điều này có an toàn hay không, nhưng tôi đã chọc qua mã nguồn cho lớp System.Web.UI.Page và sự kiện dỡ tải được kích hoạt bởi ProcessRequestCleanup riêng() trừ khi yêu cầu không đồng bộ hoặc một yêu cầu trang chéo. Cuộc gọi đến phương thức dọn dẹp nằm bên trong một khối cuối cùng được kết hợp với một khối thử xung quanh ProcessRequest. Yêu cầu quy trình đang kích hoạt tất cả các sự kiện vòng đời trang từ PreInit đến Render. Điều đó có nghĩa là Unload sẽ luôn được kích hoạt (ngoại trừ trong trường hợp async và cross page), ngay cả khi một ngoại lệ xảy ra.

Tuy nhiên tôi sẽ cảm thấy rất khó chịu khi có mã này trong các trang của tôi vì hành vi tải xuống không được ghi chính xác.

0

No. Hãy lấy ví dụ sau.

Điều gì sẽ xảy ra nếu người dùng đóng trình duyệt? Hàm Unload sau đó sẽ không được gọi và bạn sẽ có một kết nối mở với cơ sở dữ liệu.

Câu hỏi Unload này trên StackOverflow có vấn đề tương tự như bạn đang gặp phải.

+0

Phương thức tải là phía máy chủ, vì vậy chắc chắn sẽ không quan trọng những gì người dùng đã làm khi yêu cầu đã được nhận? –

+0

"Đối với chính trang, hãy sử dụng sự kiện này để thực hiện công việc dọn dẹp lần cuối, chẳng hạn như đóng các tệp đang mở và kết nối cơ sở dữ liệu hoặc hoàn tất ghi nhật ký hoặc các tác vụ yêu cầu cụ thể khác". Từ http://msdn.microsoft.com/en-us/library/ms178472.aspx. Vì vậy, bạn có thể vào một cái gì đó, nhưng sau đó một lần nữa nếu bạn nhìn vào liên kết Unload trong bài viết của tôi, bạn sẽ thấy nó không phải lúc nào cũng được gọi. Tôi sẽ không tin vào nó được gọi là, nếu có thể thay vì làm những gì Pranay Rana đề nghị, và đó sẽ là cách chính xác để xử lý kết nối của bạn. – Jethro

+0

Đây cũng là một liên kết thú vị SO http://stackoverflow.com/questions/302149/is-it-acceptable-to-keep-a-db-connection-open-for-the-life-of-the-page, có thể "Thời gian hết hạn" đang được gây ra bởi một cái gì đó khác, hãy thử sử dụng SQL Profiler để xem những gì đang diễn ra trong nền. – Jethro

2

Tôi luôn luôn tận dụng sử dụng khối soemthing như sau

using(SqlConnection) 
{ 

} 

để nó không bao giờ gây ra bất kỳ vấn đề

nếu bạn không muốn viết mã để mở kết nối một lần nữa và một lần nữa tạo ra một lớp

public class SqlConnectionManager 
{ 
    public SqlConnection GetSqlConnectionManager() 
    { 
     //create and return connection 
     //SqlConnection con = new SqlConnection(); 
     //return con; 
    } 

} 

Trong Bạn loại tệp

SqlConnection conn = null; 
using (conn = (new SqlConnectionManager()).GetSqlConnectionManager()) 
{ 
    //do work with connection 
} 

Vì vậy, theo cách trên, bạn không cần viết mã lặp đi lặp lại và cũng không cần phải viết mã để đóng kết nối vì nó tự động được xử lý bằng cách sử dụng khối.

+0

Chúc mừng, Đây chính xác là những gì tôi thường làm (và gần như chắc chắn những gì tôi sẽ làm cuối cùng một khi tôi nhận được câu hỏi của tôi trả lời). Tôi thực sự cần phải biết cụ thể liệu tôi có nên tập trung vào kịch bản cụ thể này hay không. –

1

EDIT: Theo câu trả lời của PHeiberg, điều này chắc chắn có thể xảy ra trong .net 4 và người ta có thể giả định việc dỡ bỏ sẽ luôn được gọi.

Tôi cũng đã kiểm tra mã .net 2.0 và điều này cũng đúng ở đó.

+0

Điều này không thực sự đúng (xem các nhận xét khác) –

+0

Thx để chỉ ra. – marto

1

Một (rất) kiểm tra nhanh (máy chủ web VS2010, .net v4) đã chỉ ra rằng sự kiện Unload được gọi khi ngoại lệ chưa được xử lý được tăng lên (ít nhất là khi được nâng lên trong Page_Load), nó sẽ hoạt động.

Mẫu như được liệt kê trong ví dụ này chỉ là dispose 'khi kết nối được mở.

_conn được bảo vệ, các trang được hạ xuống từ BasePage có thể tương tác với _conn và sửa đổi giá trị của nó. Có hai cách để các lớp hậu duệ phá vỡ mẫu:

  1. Gọi _conn.Close() trực tiếp. Nếu kết nối không mở, nó sẽ không được xử lý trong EndConnection.

  2. Sửa đổi giá trị _conn bằng cách đặt giá trị thành rỗng hoặc gán phiên bản DBConnection mới cho nó.

xem xét việc thay đổi phương pháp EndConnection của bạn, vì vậy _conn đó là luôn xử lý.

private void EndConnection(object sender, EventArgs e) 
{ 
    if (_conn == null) 
    { 
     return; 
    } 
    if (_conn.Connection.State == ConnectionState.Open) 
    { 
     _conn.Close(); 
    } 
    _conn.Dispose(); // always dispose even if not actually open. It may have been closed explicitly elsewhere. 
} 

Trường hợp 2 không thể bị EndConnection bắt. Cân nhắc đặt _conn riêng tư và cung cấp thuộc tính getter:

private DBConnection _conn; 

protected DBConnection Connection { 
    get 
    { 
     return _conn; 
    } 
} 

để ngăn các lớp con cháu thay đổi giá trị _conn.

Cuối cùng, bạn có phải là lớp DBConnection của riêng mình không? Tôi chỉ hỏi khi bạn trích dẫn "_conn.Connection.State" thay vì chỉ _conn.State. Nếu vậy, chỉ cần kiểm tra kỹ xem phương thức Dispose của DBConnection có hủy bỏ đúng trường hợp Connection của nó hay không.

+0

Cảm ơn những –

Các vấn đề liên quan