2012-12-15 23 views
9

Tôi biết rằng chờ đợi không thể được sử dụng trong điều khoản bắt. Tuy nhiên, tôi chưa thực sự gặp phải vấn đề nào liên quan đến điều này, cho đến bây giờ ...đang chờ bị cấm trong điều khoản bắt. Tìm kiếm một công việc arround

Về cơ bản tôi có một lớp chịu trách nhiệm nhận các yêu cầu gửi đến, xử lý chúng, tạo tin nhắn từ họ và truyền tin nhắn đến một lớp khác chịu trách nhiệm gửi tin nhắn.

Nếu xảy ra sự cố trong khi gửi thư, ngoại lệ tùy chỉnh sẽ được ném và bị lớp gửi thư gửi đi. Tại thời điểm này, một bản ghi lỗi cho thông báo này sẽ được chèn vào trong DB (mất một thời gian, do đó, không đồng bộ) và ngoại lệ sẽ được nhân lên tầng trên, phụ trách gửi phản hồi lỗi cho máy khách đã đưa ra yêu cầu .

Dưới đây là một số mã rất đơn giản với mục đích minh họa dưới đây:

public async Task ProcessRequestAsync(Request req) 
{ 
    int statusCode = 0; 

    try 
    {  
     await SendMessageAsync(new Message(req));   
    } 
    catch(MyCustomException ex) 
    { 
     statusCode = ex.ErrorCode; 
    } 

    await SendReponseToClientAsync(req, statusCode); 
} 


public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
     // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
     await InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); // CAN'T DO THAT 
     throw; 
    } 
} 

Tất nhiên lớp xử lý yêu cầu không biết gì về DB, nó chỉ là chịu trách nhiệm về việc xây dựng một thông điệp, đi qua các thông điệp tới thông điệp xử lý lớp, và gửi một phản ứng cho khách hàng (tích cực hoặc tiêu cực). Vì vậy, tôi nghĩ rằng nó có ý nghĩa ... Nếu một ngoại lệ xảy ra, lớp "kinh doanh" của tôi muốn chèn một bản ghi thất bại trong DB và rethrow ngoại lệ để các "yêu cầu" chế biến lớp có thể làm những gì cần thiết (trong này bối cảnh, gửi một phản hồi tiêu cực trở lại cho khách hàng).

Trường hợp ngoại lệ không được sử dụng theo cách này? Nó có vẻ sạch sẽ với tôi nhưng thực tế là tôi không thể làm điều này chờ đợi trong điều khoản bắt làm cho tôi nghĩ rằng có thể một mùi mã với thiết kế (ngay cả khi tôi ý tưởng xử lý ngoại lệ trong một lớp sau đó rethrowing nó cho lớp trên để làm một số xử lý khác nhau cũng như âm thanh với tôi như nó chính xác những gì ngoại lệ được thực hiện cho).

Có ý tưởng nào xảy ra với điều này không?

Cảm ơn!

+1

Có vẻ như bạn có thể bắt ngoại lệ trong lớp gửi và chỉ cần trả về đối tượng trạng thái cho người gọi. Bạn thậm chí có thể tạo một trường của đối tượng trạng thái này thuộc kiểu 'Exception' để nó có thể được ném lại sau nếu cần thiết. – dlev

+1

Tại sao bạn không chỉ gọi chức năng và không chờ đợi nó? – Rafael

+0

@dlev: Cảm ơn ý tưởng này, vẫn không cảm thấy thực sự tốt với tôi để trộn trạng thái trở lại ở một số nơi và sử dụng ngoại lệ ở nơi khác. Sự lựa chọn phải được thực hiện giữa trả lại mã trạng thái (hoặc đối tượng) hoặc ném ngoại lệ, nhưng không trộn lẫn cả hai (đó là những gì tôi nhớ từ hướng dẫn thiết kế "tốt" cho các lỗi báo cáo). – darkey

Trả lời

6

Tôi cũng đã thực hiện điều này một vài lần.

Như Rafael nhận xét, bạn chỉ có thể bỏ qua các kết quả của InsertFailureForMessageInDbAsync:

public async Task SendMessageAsync(Message msg) 
{ 
    try 
    {   
    // Some async operations done here 
    } 
    catch (MyCustomException ex) 
    {  
    var _ = InsertFailureForMessageInDbAsync(msg, ex.ErrorCode); 
    throw; 
    } 
} 

Lưu ý rằng bất kỳ trường hợp ngoại lệ từ InsertFailureForMessageInDbAsync sẽ bị bỏ qua bởi mặc định.

tùy chọn khác của bạn là phức tạp hơn:

public async Task DoSendMessageAsync(Message msg) 
{ 
    // Some async operations done here 
} 

public async Task SendMessageAsync(Message msg) 
{ 
    var task = DoSendMessageAsync(msg); 
    MyCustomException exception = null; 
    try 
    { 
    await task; 
    return; 
    } 
    catch (MyCustomException ex) 
    { 
    exception = ex; 
    } 

    await Task.WhenAll(task, InsertFailureForMessageInDbAsync(msg, exception.ErrorCode)); 
} 

này không đồng bộ sẽ xử lý các ngoại lệ và trả về một Task mà có một thực AggregateExption (có chứa cả hai trường hợp ngoại lệ nếu InsertFailureForMessageInDbAsync không ném một).

Thật không may, await sẽ bỏ qua ngoại lệ thứ hai. Nếu bạn thực sự muốn tất cả các trường hợp ngoại lệ trôi qua, bạn có thể thay thế các dòng cuối cùng (await Task.WhenAll...) với một cái gì đó như thế này:

Exception exception2 = null; 
try 
{ 
    await InsertFailureForMessageInDbAsync(msg, exception.ErrorCode); 
} 
catch (Exception ex) 
{ 
    exception2 = ex; 
} 

if (exception2 == null) 
    throw new AggregateException(exception); 
else 
    throw new AggregateException(exception, exception2); 

Nhưng đó là khá phức tạp, và không chính xác các loại mô hình mà bạn muốn lặp lại. Nếu có thể, tôi chỉ bỏ qua kết quả đăng nhập như đề xuất của Rafael.

+0

Đẹp Stephen! Có thể nói tôi đã hy vọng cho một cách dễ dàng hơn nhiều để đi arround này vì vậy tôi đã không đi xa như bạn đã làm trong reflexion. Nhưng sau tất cả, không có cách nào dễ dàng để xử lý điều này;) Cảm ơn rất nhiều câu trả lời của bạn, tôi nghĩ rằng tôi sẽ chỉ đi một cách dễ dàng hơn, nhưng ít nhất tôi sẽ biết làm thế nào để đi theo cách "cứng" nếu thực sự cần thiết. – darkey

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