none
Exception handling and EventArgs.Error

    Question

  • First thing first, I am new to Rx and hence this question can be very naive for you champs.

    I am using Rx to call wcf service from my Silverlight 5 app like this:

    IObservable<IEvent<ResonantServiceReference.SetCampusCompletedEventArgs>> observable =
    					Observable.FromEvent<ResonantServiceReference.SetCampusCompletedEventArgs>(client, "SetCampusCompleted").Take(1);
                    
                    observable.Subscribe(
    					e =>
    					{
    						if (e.EventArgs.Error == null)
    						{
    						//some logic here
    						}
    					},
    					ex => { throw ex; }
    					);
    				client.SetCampusAsync(campus, UserInformation.SecurityToken);

    Now I want to throw an exception from my service when the provided SecurityToken(that I pass when calling the service method) is wrong. However, when I throw an System.Exception from my service, it is received in e.EventArgs.Error (above). I would make this better using FaultContractAttribute at a later stage.

    My question is why doesn't the exception get caught in ex=>{ throw ex; } block (I checked it using debugger)? What is e.EventArgs.Error and how is it different from ex above? Also what is the correct way of catching exceptions (using try...catch) considering these calls are async?

    Also, I would be very thankful if someone finds anything wrong in my above code, or has a better suggestion.

    Thanks in advance


    Dharmesh

    Tuesday, December 11, 2012 11:14 PM

Answers

  • Hi Dharmesh,

    To call a WCF service asynchronously, you must generate a proxy with async support; e.g., the preferred Task-based *Async methods (.NET 4.5), legacy Begin* and End* methods (known as "APM"), or legacy *Async methods with corresponding *Completed events (known as "EBAP" or "EAP").

    It looks like you're using the latter (EBAP) since you're converting a SetCampusCompleted event into an observable and then calling the SetCampusAsync method.

    The type of the event arguments (ResonantServiceReference.SetCampusCompletedEventArgs, in your example) is part of the generated code for the client proxy.  The event arguments class derives from AsyncCompletedEventArgs, which defines the Error property to which you are referring.

    So the Error property has nothing to do with Rx.  Rx doesn't even know about it.

    When you call Subscribe, you are passing in a callback for OnNext and a callback for OnError.  However, the Observable.FromEvent method generates an observable that will never call OnError or OnCompleted.  Think of FromEvent as simply adding an event handler that calls OnNext whenever the event is raised.  CLR events don't provide a native model for indicating that an error has occurred or that an event will not be raised again, at least not through direct invocation.  To raise an error or completion, one of two models is typically used:

    1. Define separate events; e.g., MyEventError and MyEventCompleted.
      -or-
    2. Include the information in the EventArgs.

    WCF uses the second strategy.

    In your query, you are converting an event completion notification into a call to OnCompleted by using the Take(1) operator.  No extra data is needed.

    If you want to move SetCampusCompletedEventArgs.Error into the OnError callback, then you must do that yourself as well.

    For example, you could write a SelectMany query that uses a conditional operator and Throw.  Or just use the extension method that is already defined in Rxx for exactly this purpose:

    Observable2.FromEventBasedAsyncPattern

    I don't have a lab for this yet, but I've already made a note to create one.

    Basically, you should call it like this: 

    var setCampus = Observable2.FromEventBasedAsyncPattern(
    	eh => eh,
    	eh => client.SetCampusCompleted += eh,
    	eh => client.SetCampusCompleted -= eh,
    	state => client.SetCampusAsync(campus, UserInformation.SecurityToken, state),
    	() => { });
    
    setCampus.Subscribe(e => { /*No Error*/ }, ex => { /*Error*/ });

    - Dave


    http://davesexton.com/blog

    • Edited by Dave Sexton Wednesday, December 12, 2012 5:37 AM Added call to Subscribe in example; Small clarification
    • Marked as answer by Dharmesh1 Wednesday, December 12, 2012 6:02 AM
    Wednesday, December 12, 2012 5:34 AM

All replies

  • Hi Dharmesh,

    To call a WCF service asynchronously, you must generate a proxy with async support; e.g., the preferred Task-based *Async methods (.NET 4.5), legacy Begin* and End* methods (known as "APM"), or legacy *Async methods with corresponding *Completed events (known as "EBAP" or "EAP").

    It looks like you're using the latter (EBAP) since you're converting a SetCampusCompleted event into an observable and then calling the SetCampusAsync method.

    The type of the event arguments (ResonantServiceReference.SetCampusCompletedEventArgs, in your example) is part of the generated code for the client proxy.  The event arguments class derives from AsyncCompletedEventArgs, which defines the Error property to which you are referring.

    So the Error property has nothing to do with Rx.  Rx doesn't even know about it.

    When you call Subscribe, you are passing in a callback for OnNext and a callback for OnError.  However, the Observable.FromEvent method generates an observable that will never call OnError or OnCompleted.  Think of FromEvent as simply adding an event handler that calls OnNext whenever the event is raised.  CLR events don't provide a native model for indicating that an error has occurred or that an event will not be raised again, at least not through direct invocation.  To raise an error or completion, one of two models is typically used:

    1. Define separate events; e.g., MyEventError and MyEventCompleted.
      -or-
    2. Include the information in the EventArgs.

    WCF uses the second strategy.

    In your query, you are converting an event completion notification into a call to OnCompleted by using the Take(1) operator.  No extra data is needed.

    If you want to move SetCampusCompletedEventArgs.Error into the OnError callback, then you must do that yourself as well.

    For example, you could write a SelectMany query that uses a conditional operator and Throw.  Or just use the extension method that is already defined in Rxx for exactly this purpose:

    Observable2.FromEventBasedAsyncPattern

    I don't have a lab for this yet, but I've already made a note to create one.

    Basically, you should call it like this: 

    var setCampus = Observable2.FromEventBasedAsyncPattern(
    	eh => eh,
    	eh => client.SetCampusCompleted += eh,
    	eh => client.SetCampusCompleted -= eh,
    	state => client.SetCampusAsync(campus, UserInformation.SecurityToken, state),
    	() => { });
    
    setCampus.Subscribe(e => { /*No Error*/ }, ex => { /*Error*/ });

    - Dave


    http://davesexton.com/blog

    • Edited by Dave Sexton Wednesday, December 12, 2012 5:37 AM Added call to Subscribe in example; Small clarification
    • Marked as answer by Dharmesh1 Wednesday, December 12, 2012 6:02 AM
    Wednesday, December 12, 2012 5:34 AM
  • Thanks a lot Dave for your wonderful explanation.

    I have one more question (encouraged by your response), but I will create a separate thread for it.

    Thanks again,

    Dharmesh


    Dharmesh

    Wednesday, December 12, 2012 6:07 AM