2011-12-15 26 views
8

Tôi đã đọc Tôi cần triển khai IDisposable nếu lớp của tôi có biến thành viên tự IDisposable. Vâng, tôi đang thực hiện giao diện IDisposable vì lớp học của tôi chứa một đối tượng cơ sở dữ liệu (db lớp thành viên dưới đây) mà là IDisposable bản thân:Xử lý ngoại lệ trong hàm dựng khi triển khai IDisposable

public class CommissionModel : IDisposable 
    { 
     protected PetaPoco.Database db; 

     public CommissionModel() 
     { 
      string connectionString = "Server=localhost;..."; 

      // The line below may throw an exception (!!!) 
      db = new PetaPoco.Database(connectionString, "mysql");    
     } 

     // Automatically close database connection 
     public void Dispose() 
     { 
      if (db != null) 
       db.Dispose(); 

      db = null; 
     } 

     public void InsertRecord(Record somerecord) 
     { 
      // ... 
      db.Insert(somerecord); 
     } 

Vấn đề là instantiation của db thành viên có thể thất bại trong một số hoàn cảnh. Tôi nên làm gì khi ngoại lệ được ném vào hàm tạo và đối tượng cơ sở dữ liệu không được tạo? Tôi có nên rethrow ngoại lệ hoặc có thể kiểm tra nếu db != null trong phương pháp InsertRecord?

+0

IMO bạn nên tính lại ngoại lệ. Bạn có thể bọc exepction ban đầu vào một số cấp cao hơn, để có lớp cách ly tốt hơn. Tốt hơn là nên thông báo rằng có điều gì đó không ổn. – Krzysztof

+0

Hãy xem [RẮN] (https://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29), cụ thể tại [SRP] (https://en.wikipedia.org/wiki/Single_responsibility_principle) và [DI] (https://en.wikipedia.org/wiki/Dependency_inversion_principle) – oleksii

Trả lời

1

Bạn chỉ nên nắm bắt các ngoại lệ mà bạn có thể xử lý. Từ mã, có vẻ như khởi tạo biến db có thể cho thấy có sự cố khi kết nối với máy chủ cơ sở dữ liệu của bạn.

Đề xuất của tôi là giữ nguyên mã của bạn và xử lý các ngoại lệ ở vị trí trung tâm như sự kiện Application_Error trong Global.asax hoặc sự kiện đang khởi tạo CommissionModel.

2

Lý tưởng nhất là bạn không nên làm bất kỳ "công việc" nào trong hàm tạo. Làm việc trong hàm dựng nên thực sự liên kết các tham chiếu đối tượng. Điều này sẽ cho phép bạn kiểm tra đơn vị lớp này bằng cách kết nối các lớp giả.

Hãy thử điều này thay vì:

public CommissionModel(PetaPoco.Database db) { 
    this.db = db; 
} 
+1

+1 Đây là câu trả lời hay nhất mà điểm OP hướng tới thiết kế tốt hơn. Tôi cũng sẽ đề cập đến nguyên tắc SOLID và có thể sử dụng 'IDatabase' thay vì' PetaPoco.Database' – oleksii

+0

@oleksii, tôi phải không đồng ý với bạn về điều đó. Lý do đằng sau việc thực hiện 'CommissionModel' là cô lập tất cả các thao tác dữ liệu mức thấp và chức năng xử lý cơ sở dữ liệu từ các phần khác của mã. Hơn nữa, một ngày nào đó tôi có thể quyết định chuyển sang một khuôn khổ ORM khác thay vì PetaPoco. – ezpresso

+0

@ezpresso * "[...] một ngày tôi có thể quyết định chuyển sang khung ORM khác thay vì PetaPoco" * chính xác là tại sao bạn có thể cần phải vượt qua một giao diện, được thực hiện với PetaPoco ngay bây giờ, và sau đó, nên bạn muốn, bạn có thể thay đổi thành ORM khác. Tôi không chắc chắn tôi theo bạn trong một cảm giác với những gì cụ thể mà bạn không đồng ý với. Bạn có thể xin giải thích cho tôi không? – oleksii

3

Nếu constructor của bạn ném một ngoại lệ, người tiêu dùng của mã của bạn sẽ không bao giờ nhận được một tham chiếu đến một thể hiện của lớp học của bạn. Bộ nhớ lớp khởi tạo một phần cuối cùng sẽ được thu thập và Dispose sẽ không được gọi.

Tôi thường khuyên bạn nên di chuyển một thao tác có thể thất bại do hoàn cảnh bên ngoài (và không phải do lạm dụng, như giá trị thông số không hợp lệ) cho phương pháp riêng, như "Kết nối".

2

Có vẻ như bạn không phải làm gì cả.

Nếu quá trình xây dựng của bạn bị gián đoạn, tài nguyên không được tạo. Trong hầu hết các tình huống sử dụng (điều này đặt bên ngoài lớp học của bạn), Dispose() sẽ không bao giờ được gọi. Nhưng ngay cả khi nó được, mã số if (db != null) của bạn là đủ bảo vệ.

Một vài điểm nhỏ:

  • các db = null; bên trong Dispose() là vô nghĩa.
  • InsertRecord() nên bắt đầu bằng cách kiểm tra if (db != null)
1

Nếu không có cách nào mà một ngoại lệ có thể xảy ra (*) giữa thời gian các nhà xây dựng có được một nguồn tài nguyên và thời gian nó được quay trở lại ứng dụng, không có gì phải lo lắng là trong khoảng.

Nếu nó có thể so với một ngoại lệ có thể xảy ra trong hoàn cảnh như vậy, tôi sẽ đề nghị một mô hình như:

 
public class CommissionModel : IDisposable 
    { 
     protected PetaPoco.Database db; 
     protected OtherResourceType resource2; 

     public CommissionModel() 
     { 
      Boolean ok; 
      string connectionString = "Server=localhost;..."; 

      ok = false; 
      try 
      { 
       // Either of the next two lines may throw an exception 
       db = new PetaPoco.Database(connectionString, "mysql"); 
       resource2 = new OtherResourceType(); 
       // Once we make it this far, we should be successful 
       ok = true; 
      } 
      finally 
      { 
       if (!ok) 
        Dispose(); 
      } 
     } 

     // Automatically close database connection 
     public void Dispose() 
     { 
      Zap(ref db); // Method to Dispose and null out, only if not null 
      Zap(ref resource2); 
     } 
    } 

(*) Đó là hầu như luôn luôn có thể cho một ThreadAbortException xảy ra nếu một số có nghĩa là yêu tinh khó chịu gọi Tread. Hủy bỏ trên mã của bạn, nhưng thực sự không có gì để được thực hiện về điều đó trong mọi trường hợp.