2010-05-19 34 views
7

Phương thức của tôi gọi SQL Server trả về DataReader nhưng vì những gì tôi cần làm - trả lại DataReader cho phương thức gọi nằm trong mã phía sau - Tôi không thể đóng kết nối trong lớp của phương thức gọi máy chủ SQL. Do điều này, tôi không có cuối cùng hoặc sử dụng các khối.Tôi có nên triển khai IDisposable tại đây không?

Là cách xử lý đúng tài nguyên để lớp học triển khai IDisposable? Ngoài ra, tôi có nên xử lý rõ ràng tài nguyên không được quản lý (các trường cấp lớp) khỏi người gọi không?

EDIT: Tôi gửi datareader trở lại bởi vì tôi cần để ràng buộc dữ liệu cụ thể từ datareader đến một điều khiển listitem, vì vậy trong lớp gọi (codebehind trang), tôi làm:

new ListItem(datareader["dc"]); (along those lines). 
+9

Tại sao bạn muốn gửi người đọc dữ liệu vào trang? – Perpetualcoder

+0

Trả về DataReader trực tiếp có thể là thực hành không tốt, nhưng nó có thể hữu ích cho anh ta trong một số trường hợp. – Venemo

+0

@Venemo - Tôi nghĩ rằng http://stackoverflow.com/questions/2867661/should-i-implement-idisposable-here/2869503#2869503 có thể phục vụ anh ấy tốt hơn. – dss539

Trả lời

7

tôi sẽ nói có , triển khai IDisposable. Một trong những lý do chính xa như tôi có thể nói để sử dụng nó là khi bạn không thể tin tưởng người dùng của đối tượng đủ để làm điều đó một cách chính xác. Điều này dường như là một ứng cử viên chính cho điều đó.

Tuy nhiên, điều này đang được nói đến, có một câu hỏi đối với kiến ​​trúc của bạn. Tại sao bạn muốn tự mình gửi DataReader đến trang thay vì gọi phương thức để làm điều đó cho bạn (bao gồm dọn dẹp có liên quan) bằng cách trả lại những gì cần thiết? Nếu nó cần thiết để cung cấp cho người đọc thực sự vào trang, thì cũng vậy.

3

Có, bạn nên triển khai IDisposable trên lớp tùy chỉnh của mình nếu nó chứa Trình quản lý dữ liệu mở khi quay trở lại lớp thấp hơn.

Đây là mẫu được chấp nhận khi trả lại thứ gì đó, cần được làm sạch.

2

lớp học của bạn

class MyClass : IDisposable 
{ 
    protected List<DataReader> _readers = new List<DataReader>(); 
    public DataReader MyFunc() 
    { 
     ///... code to do stuff 

     _readers.Add(myReader); 
     return myReader; 
    } 
    private void Dispose() 
    { 
     for (int i = _readers.Count - 1; i >= 0; i--) 
     { 
      DataReader dr = _reader.Remove(i); 
      dr.Dispose(); 
     } 
     _readers = null; 

     // Dispose/Close Connection 
    } 
} 

Sau đó, bên ngoài lớp học của bạn

public void FunctionThatUsesMyClass() 
{ 
    using(MyClass c = new MyClass()) 
    { 
     DataReader dr = c.MyFunc(); 
    } 
} 

Tất cả các độc giả và MyClass dụ được dọn dẹp khi using thoát khối.

+0

Tại sao bận tâm loại bỏ chúng khỏi biến '_readers'? Và thiết lập nó '= null' sẽ không thực sự làm gì cả. –

+0

Loại bỏ chúng khỏi _readers là một cách tuyệt vời để de-tham khảo chúng, về cơ bản nói với GC rằng nó có thể làm sạch chúng. – Venemo

+0

'_readers = null' chỉ là do thói quen. Bạn không cần nó bởi vì khi đối tượng 'MyClass' là GC'ed, do đó sẽ là List trống. Xóa chúng khỏi đối tượng _readers giảm số lượng tham chiếu và sẽ hỗ trợ trong Bộ sưu tập rác, cuối cùng GC sẽ nhận ra các tham chiếu duy nhất đối với chúng là từ các đối tượng đang chờ xử lý, nhưng không thể làm tổn thương. – Aren

3

Trước tiên, hãy chuyển số DataReader có thể không thực sự là những gì bạn muốn làm, nhưng tôi sẽ cho rằng đó là.

Cách thích hợp để xử lý việc này là trả lại loại hỗn hợp bao gói hoặc để lộ DataReader và giữ kết nối, sau đó triển khai IDisposable trên loại đó. Khi loại bỏ loại đó, vứt bỏ cả đầu đọc và kết nối.

public class YourClass : IDisposable 
{ 
    private IDbConnection connection; 
    private IDataReader reader; 

    public IDataReader Reader { get { return reader; } } 

    public YourClass(IDbConnection connection, IDataReader reader) 
    { 
     this.connection = connection; 
     this.reader = reader; 
    } 

    public void Dispose() 
    { 
     reader.Dispose(); 
     connection.Dispose(); 
    } 
} 
4

Giữ kết nối cơ sở dữ liệu dưới dạng biến thành viên trong lớp người đọc của bạn và làm cho lớp người đọc của bạn triển khai IDisposable có vẻ ổn với tôi.

Tuy nhiên, bạn có thể xem xét đưa phương thức của mình trở về IEnumerable và sử dụng các câu lệnh yield return để đọc qua trình đọc dữ liệu. Bằng cách đó bạn có thể trả lại kết quả và vẫn dọn sạch từ bên trong phương thức của mình.

Dưới đây là một phác thảo sơ bộ những gì tôi có nghĩa là:

public IEnumerable<Person> ReadPeople(string name) 
{ 
    using (var reader = OpenReader(...)) 
    { 
     // loop through the reader and create Person objects 
     for ... 
     { 
      var person = new Person(); 
      ... 
      yield return person; 
     } 
    } 
} 
+0

...hoặc cách khác, "công khai IEnumerable " với "năng suất trả lại người đọc;" và để nó cho người gọi cách xử lý dữ liệu. – Joe

+0

Tôi đã làm rất giống trong quá khứ. Tôi genericised nó xuống đến một lớp học mà chỉ đóng gói kết nối và người đọc với nhau - nhưng bạn có thể cascade chúng (vì vậy bạn vẫn có thể có nhiều độc giả). Làm việc khá độc đáo – philsquared

1

Nguyên tắc chung là lớp học của bạn nên thực hiện IDisposable nếu nó trực tiếp nắm giữ các nguồn lực không được quản lý hoặc giữ một tham chiếu đến một đối tượng IDisposable. Nếu lớp của bạn tạo ra một phương thức IDataReader nhưng không bao giờ giữ tham chiếu đó thì lớp của bạn sẽ không cần phải thực hiện IDisposable cho mỗi quy tắc (trừ khi nó chỉ xảy ra để giữ IDisposable ngoài IDataReader được tạo trong phương thức đó).

Câu hỏi thực sự bạn cần tự hỏi là liệu lớp học của bạn có thực sự nên giữ trên đó IDataReader ngay cả sau khi nó đã gửi nó cho người gọi. Cá nhân, tôi nghĩ rằng đó là một thiết kế kém bởi vì nó làm mờ dòng sở hữu. Ai thực sự sở hữu IDisposable trong trường hợp đó? Ai chịu trách nhiệm cho cuộc đời của mình? Lấy ví dụ về các lớp học IDbCommand. Họ tạo ra các trường hợp IDataReader và trả lại cho những người gọi, nhưng tự hủy bỏ quyền sở hữu. Điều đó làm cho API sạch sẽ và trách nhiệm quản lý lâu dài là rõ ràng trong trường hợp đó.

Bất kể vấn đề quyền sở hữu nào, tình huống cụ thể của bạn cần phải triển khai IDisposable; không phải vì lớp học của bạn xảy ra để tạo và trả về một phiên bản IDataReader, nhưng bởi vì nó có vẻ như nó giữ một đối tượng IDbConnection.

2

Tôi sẽ không trả lại bất cứ điều gì. Thay vào đó, tôi sẽ vượt qua một đại biểu.

Ví dụ:

void FetchMeSomeReader(Action<IDataReader> useReader) 
{ 
    using(var reader = WhateverYouDoToMakeTheReader()) 
     useReader(reader); 
} 

Sau đó, trong lớp gọi điện thoại của bạn:

void Whatever() 
{ 
    FetchMeSomeReader(SetFields); 
} 

void SetFields(IDataReader reader) 
{ 
    MyListItem = new ListItem(datareader["dc"]); 
} 
Các vấn đề liên quan