2008-11-10 52 views
34

Trong một ứng dụng tôi làm việc trên, bất kỳ lỗi logic nghiệp vụ nào cũng gây ra ngoại lệ và mã gọi xử lý ngoại lệ. Mẫu này được sử dụng trong suốt ứng dụng và hoạt động tốt.Ném nhiều ngoại lệ trong .Net/C#

Tôi có một tình huống mà tôi sẽ cố gắng thực hiện một số tác vụ kinh doanh từ bên trong lớp doanh nghiệp. Yêu cầu cho điều này là một thất bại của một nhiệm vụ không nên gây ra quá trình chấm dứt. Các nhiệm vụ khác vẫn có thể thực thi. Nói cách khác, đây không phải là một hoạt động nguyên tử. Vấn đề tôi có là ở phần cuối của hoạt động, tôi muốn thông báo cho mã gọi rằng một ngoại lệ hoặc ngoại lệ đã xảy ra bằng cách ném một ngoại lệ. Hãy xem đoạn mã psuedo-code sau:

function DoTasks(MyTask[] taskList) 
{ 
    foreach(MyTask task in taskList) 
    { 
    try 
    { 
     DoTask(task); 
    } 
    catch(Exception ex) 
    { 
     log.add(ex); 
    } 
    } 

    //I want to throw something here if any exception occurred 
} 

Tôi phải làm gì? Tôi đã gặp phải mô hình này trước đây trong sự nghiệp của tôi. Trong quá khứ tôi đã giữ một danh sách tất cả các ngoại lệ, sau đó ném một ngoại lệ có chứa tất cả các ngoại lệ bị bắt. Điều này dường như không phải là cách tiếp cận trang nhã nhất. Điều quan trọng là phải bảo toàn càng nhiều chi tiết càng tốt từ mỗi ngoại lệ để trình bày đến mã gọi.

Suy nghĩ?


Chỉnh sửa: Phải viết giải pháp .Net 3.5. Tôi không thể sử dụng bất kỳ thư viện beta nào, hoặc AggregateException trong .Net 4.0 như được đề cập bởi Bradley Grainger (bên dưới) sẽ là một giải pháp tốt đẹp cho các ngoại lệ thu thập để ném.

+0

Chỉ cần trả lại ngoại lệ. Sau khi bạn đăng nhập nó (và làm bất cứ điều gì khác), rethrow nó để nó bong bóng lên ngăn xếp. – Kon

+1

Ngoại lệ nào? Kịch bản của tôi giả định nhiều ngoại lệ. –

+0

Tôi vừa cập nhật câu trả lời của mình để lưu ý rằng bạn có thể sao chép AggregateException trên .NET 3.5 theo cách giúp dễ dàng nâng cấp lên loại .NET 4.0 thực khi nó được phát hành. Bạn vẫn phải viết mã (để nhân bản lớp), nhưng nó tương thích về phía trước. –

Trả lời

37

Các Task Parallel Library extensions cho NET (mà will become part of .NET 4.0) theo mô hình đề xuất trong câu trả lời khác: thu thập tất cả trường hợp ngoại lệ đã được ném vào một lớp AggregateException.

Bằng cách luôn ném cùng một loại (cho dù có một ngoại lệ từ công việc con, hoặc nhiều), mã gọi xử lý ngoại lệ dễ viết hơn.

Trong .NET 4.0 CTP, AggregateException có công cụ xây dựng công khai (mất IEnumerable<Exception>); nó có thể là một lựa chọn tốt cho ứng dụng của bạn.

Nếu bạn đang nhắm mục tiêu .NET 3.5, hãy xem xét nhân bản các phần của lớp System.Threading.AggregateException mà bạn cần trong mã của riêng bạn, ví dụ: một số hàm tạo và thuộc tính InnerExceptions. (Bạn có thể đặt bản sao của bạn trong không gian tên System.Threading bên trong lắp ráp của bạn, có thể gây nhầm lẫn nếu bạn tiếp xúc công khai, nhưng sẽ nâng cấp lên 4.0 dễ dàng hơn sau này.) Khi .NET 4.0 được phát hành, bạn sẽ có thể “nâng cấp” vào loại khung bằng cách xóa tệp nguồn chứa bản sao của bạn khỏi dự án của bạn, thay đổi dự án để nhắm mục tiêu phiên bản khung công tác mới và xây dựng lại. Tất nhiên, nếu bạn làm điều này, bạn cần phải theo dõi cẩn thận các thay đổi đối với lớp này khi Microsoft phát hành các CTP mới, để mã của bạn không trở nên không tương thích. (Ví dụ, điều này có vẻ như một lớp học có mục đích chung hữu ích, và họ có thể di chuyển nó từ System.Threading đến System.) Trong trường hợp xấu nhất, bạn chỉ có thể đổi tên loại và di chuyển nó trở lại vào không gian tên của riêng bạn (điều này rất dễ dàng với hầu hết các công cụ tái cấu trúc).

+0

Tôi ước gì chúng tôi đã có thư viện này. Tôi đã rối tung với nó một chút ở bên cạnh, và nó thật tuyệt vời. Thật không may, chúng tôi đang nhắm mục tiêu .Net 3.5. –

+0

Thực ra, bạn có thể nhận được thư viện ParallelFx ngay bây giờ. http://www.microsoft.com/downloads/details.aspx?FamilyId=348F73FD-593D-4B3C-B055-694C50D2B0F3&displaylang=vi – Will

+0

Tôi sẽ không đưa nó vào sản xuất. Đó là một CTP. –

0

Không có giải pháp siêu thanh lịch ở đây nhưng một vài ý tưởng:

  • Vượt qua một hàm lỗi-handler như là đối số để DoTasks vì vậy người dùng có thể quyết định có nên tiếp tục
  • Sử dụng truy tìm để đăng nhập lỗi khi chúng xảy ra
  • cONCATENATE các thông điệp từ các trường hợp ngoại lệ khác trong thông điệp bó ngoại lệ của
2

bạn có thể muốn sử dụng một BackgroundWorker để làm điều này cho bạn. Nó sẽ tự động chụp và trình bày bất kỳ ngoại lệ nào khi hoàn thành, sau đó bạn có thể ném hoặc đăng nhập hoặc làm bất cứ điều gì. Ngoài ra, bạn có được lợi ích của đa luồng.

Các BackgroundWorker là một wrapper đẹp xung quanh đại biểu của asynchronous programming model.

+1

Đó là một ý tưởng hay, nhưng vấn đề tập hợp và tái cấu trúc vẫn tồn tại. Ngoài ra, mã psuedo của tôi là một sự đơn giản hóa của một kịch bản đa luồng thực tế, trong đó nhiều ngoại lệ có thể được lấy ra từ các chủ đề nền. Tôi chỉ không muốn nhầm lẫn vấn đề với thông tin đó. –

4

Bạn có thể tạo ra một ngoại lệ tùy chỉnh mà chính nó có một tập hợp các trường hợp ngoại lệ. Sau đó, trong khối Catch của bạn, chỉ cần thêm nó vào bộ sưu tập đó. Vào cuối quá trình của bạn, kiểm tra xem số ngoại lệ là> 0, sau đó ném ngoại lệ tùy chỉnh của bạn.

+1

Đó là câu trả lời được đưa ra trong câu hỏi. – asterite

13

Hai cách đỉnh đầu của tôi sẽ là một trong hai làm một ngoại lệ tùy chỉnh và thêm các ngoại lệ đối với lớp này và vứt rằng cuối cùng:

public class TaskExceptionList : Exception 
{ 
    public List<Exception> TaskExceptions { get; set; } 
    public TaskExceptionList() 
    { 
     TaskExceptions = new List<Exception>(); 
    } 
} 

    public void DoTasks(MyTask[] taskList) 
    { 
     TaskExceptionList log = new TaskExceptionList(); 
     foreach (MyTask task in taskList) 
     { 
      try 
      { 
       DoTask(task); 
      } 
      catch (Exception ex) 
      { 
       log.TaskExceptions.Add(ex); 
      } 
     } 

     if (log.TaskExceptions.Count > 0) 
     { 
      throw log; 
     } 
    } 

hoặc trả lại đúng hay sai nếu nhiệm vụ thất bại và có biến 'danh sách ngoài'.

public bool TryDoTasks(MyTask[] taskList, out List<Exception> exceptions) 
    { 
     exceptions = new List<Exception>(); 
     foreach (MyTask task in taskList) 
     { 
      try 
      { 
       DoTask(task); 
      } 
      catch (Exception ex) 
      { 
       exceptions.Add(ex); 
      } 
     } 

     if (exceptions.Count > 0) 
     { 
      return false; 
     } 
     else 
     { 
      exceptions = null; 
      return true; 
     } 
    } 
+0

Giải pháp thứ hai của bạn cảm thấy phù hợp với tôi. –

+1

Tôi thích giải pháp thứ hai, nhưng dự án tôi đang làm việc có "ném một ngoại lệ cho vi phạm quy tắc kinh doanh" được tích hợp ở mọi nơi. Điều này thực sự sẽ vi phạm mẫu đó. –

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