2011-10-19 5 views
12

Vấn đề:Có thể trong .Net để bắt tất cả ngoại lệ chưa được xử lý từ bất kỳ phương thức nào trong một lớp trước khi nó được chuyển lên ngăn xếp cuộc gọi không?

Tôi muốn bắt bất kỳ trường hợp ngoại lệ từ phương pháp nào trong một lớp học để tôi có thể ghi lại các dữ liệu cụ thể lớp học để các ngoại lệ đối với khai thác gỗ trước khi nó được chuyển lên stack. Tôi biết rằng tôi có thể đặt một thử-catch trong mọi phương pháp của lớp, nhưng có rất nhiều phương pháp và có vẻ như có nên có một cách hiệu quả hơn.

Ví dụ về những gì tôi hiện đang thực hiện:

public class ClassA 
{ 
    private int x; 
    private int y; 

    public void Method1() 
    { 
     try 
     { 
      //Some code 
     } 
     catch(Exception ex) 
     { 
      ex.Data.Add("x", x); 
      ex.Data.Add("y", y); 
      throw; 
     } 
    } 

    public void Method2() 
    { 
     try 
     { 
      //Some code 
     } 
     catch (Exception ex) 
     { 
      ex.Data.Add("x", x); 
      ex.Data.Add("y", y); 
      throw; 
     } 
    } 
} 

Ví dụ về những gì tôi muốn làm:

public class ClassB : IUnhandledErrorHandler 
{ 
    private int x; 
    private int y; 

    public void Method1() 
    { 
     //Some code 
    } 

    public void Method2() 
    { 
     //Some code 
    } 

    void IUnhandledErrorHandler.OnError(Exception ex) 
    { 
     ex.Data.Add("x", x); 
     ex.Data.Add("y", y); 
     throw; 
    } 
} 

public interface IUnhandledErrorHandler 
{ 
    void OnError(Exception ex); 
} 

Lưu ý: Lớp này là một dịch vụ trong một dự án WCF và triển khai ServiceContract. Tôi đã thử thêm một ErrorHandler vào ChannelDispatcher của dịch vụ. Tuy nhiên, khi lỗi đến ErrorHandler nó đã vượt quá phạm vi của lớp nơi xảy ra lỗi, vì vậy tôi không thể truy cập các chi tiết lớp học.

Giải pháp:

public class ClassC 
{ 
    public ClassC() 
    { 
     AppDomain.CurrentDomain.FirstChanceException += OnError; 
    } 

    private int x; 
    private int y; 

    public void Method1() 
    { 
     //Some code 
    } 

    public void Method2() 
    { 
     //Some code 
    } 

    private void OnError(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e) 
    { 
     e.Exception.Data["x"] = x; 
     e.Exception.Data["y"] = y; 
    } 
} 
+0

Đây có phải là chỉ để gỡ lỗi không? Bạn có muốn gửi ngoại lệ cho khách hàng hay chỉ giữ chúng ở bên máy chủ không? –

+0

của nó để gỡ lỗi/đăng nhập ở phía máy chủ. – KowalskiTom

+4

Bạn không bao giờ nên sử dụng "throw ex;". Điều đó làm cho nó trông giống như ngoại lệ ban đầu đến từ vị trí của "ném cũ;". –

Trả lời

14

Nếu bạn chạy trên .NET 4, bạn có thể sử dụng sự kiện FirstChanceException từ AppDomain.

+0

Điều này có vẻ đầy hứa hẹn. Tôi sẽ cố gắng để thực hiện nó để xem nếu nó là một giải pháp khả thi. – KowalskiTom

+0

Hoạt động, Cảm ơn bạn – KowalskiTom

+0

Tốt :-) Vui mừng khi biết bạn đã đạt được điều đó :-) – Seb

0

Bạn có thể thực hiện điều này bằng cách kế thừa từ System.ContextBoundObject. Nó không đẹp như bạn mong đợi, và tôi tin rằng có lẽ có một số chi phí đáng kể liên quan đến nó, nhưng theo như tôi biết đó là cách duy nhất để làm những gì bạn mô tả.

Như được yêu cầu ở đây là việc xây dựng thêm ... như đã đề cập, điều này không đẹp, nhưng đây là một sự triển khai tối thiểu cần thiết để thực hiện công việc này. Phương thức quan tâm chính là phương thức AOPSink.SyncProcessMessage. Nó được gọi trước bất kỳ phương thức nào trên đối tượng 'Test', và lời gọi tới NextSink.SyncProcessMessage (msg) là những gì thực sự gọi phương thức ban đầu được gọi. Bạn có thể kiểm tra (và sửa đổi) phương thức nào được gọi và tham số truyền bằng cách chơi với tham số IMessage được chuyển đến SyncProcessMessage, và bạn có thể kiểm tra/sửa đổi giá trị trả về (hoặc ném ngoại lệ) bằng cách chơi với IMessage được trả về từ NextSink.SyncProcessMessage. Có hiệu suất đáng kể trên không cho chức năng này, vì vậy tôi sẽ không khuyên bạn nên nó cho các đối tượng lưu lượng truy cập cao bên ngoài mục đích gỡ lỗi.

[AOP] 
class Test : ContextBoundObject 
{  
    public void TestMethod()  
    { 
     throw new Exception(); 
    }  
} 

[AttributeUsage(AttributeTargets.Class)] 
public class AOPAttribute : ContextAttribute 
{ 
    public AOPAttribute() : base("AOPAttribute") { } 
    public override void GetPropertiesForNewContext(IConstructionCallMessage ctor) 
    { 
     ctor.ContextProperties.Add(new AOPProperty()); 
    } 
    public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg) 
    { 
     return false; 
    } 
} 

public class AOPProperty : IContextProperty, IContributeServerContextSink 
{ 
    public IMessageSink GetServerContextSink(IMessageSink nextSink) 
    { 
     return new AOPSink(nextSink); 
    } 
    public string Name { get { return "AOP"; } } 
    public bool IsNewContextOK(Context ctx) { return true; } 
    public void Freeze(Context ctx) { } 
} 

public class AOPSink : IMessageSink 
{ 
    public AOPSink(IMessageSink nextSink) { this.NextSink = nextSink; } 
    public IMessageSink NextSink { get; private set; } 
    public IMessage SyncProcessMessage(IMessage msg) 
    { 
     // inspect method+params by playing with 'msg' here 
     IMessage m = NextSink.SyncProcessMessage(msg); 
     // inspect returnval/exception by playing with 'm' here 
     return m; 
    } 
    public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink) 
    { 
     throw new NotSupportedException(); 
    } 
} 
+0

Bạn có thể vui lòng xây dựng? Tôi không tin rằng đối tượng có bất kỳ phương tiện nào để giải quyết câu hỏi của OP. – CodeNaked

+0

Điều này có thể là quá nhiều chi phí, vì nó sẽ được sử dụng trong sản xuất để đăng nhập. Ngoài ra, có thể truy cập vào các trường của lớp mà từ đó lỗi được đưa ra không? – KowalskiTom

+0

Chắc chắn, bạn có thể gói gọn tất cả công cụ Context + Sink trong lớp cơ sở với phương thức trừu tượng 'OnError' sẽ được gọi từ SyncProcessMessage .... để bạn có thể xử lý quyền ngoại lệ ngay trong lớp đã ném nó (giống như ví dụ 'những gì tôi muốn làm') – redec