Async Command Template - Exception Handling

I am implementing an asynchronous command template for the client class in a client / server application. In the past, I did some encoding of sockets, and I like the new Async pattern that they used in the Socket / SocketAsyncEventArgs classes.

My async method is as follows: public bool ExecuteAsync(Command cmd);It returns true if execution is pending and false if it is completed synchronously. My question is: should I always call a callback (cmd.OnCompleted) even in case of an exception? Or should I throw exceptions from ExecuteAsync?

Here are some details if you need them. This is similar to using SocketAsyncEventArgs, but instead of SocketAsyncEventArgs, my class is called SomeCmd.

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!");
cmd.OnCompleted += this.SomeCmd_OnCompleted;
this.ConnectionToServer.ExecuteAsync(cmd);

As with the Socket, if you need to coordinate your callback method (SomeCmd_OnCompleted in this case), you can use the return value of ExecuteAsync to find out if the operation is expecting (true) or if the operation completed synchronously.

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!");
cmd.OnCompleted += this.SomeCmd_OnCompleted;
if( this.ConnectionToServer.ExecuteAsync(cmd) )
{
    Monitor.Wait( this.WillBePulsedBy_SomeCmd_OnCompleted );
}

Here is a very simplified version of my base classes, but you can see how this works:

class Connection
{
    public bool ExecuteAsync(Command cmd)
    {
        /// CONSIDER: If you don't catch every exception here
        /// then every caller of this method must have 2 sets of
                /// exception handling:
        /// One in the handler of Command.OnCompleted and one where ExecuteAsync
        /// is called.
        try
        {
        /// Some possible exceptions here:
        /// 1) remote is disposed. happens when the other side disconnects (WCF).
        /// 2) I do something wrong in TrackCommand (a bug that I want to fix!)
            this.TrackCommand(cmd);
            remote.ServerExecuteAsync( cmd.GetRequest() );
            return true;
        }
        catch(Exception ex)
        {
            /// Command completing synchronously.
            cmd.Completed(ex, true);
            return false;
        }
    }
    /// <summary>This is what gets called by some magic when the server returns a response.</summary>
    internal CommandExecuteReturn(CommandResponse response)
    {
        Command cmd = this.GetTrackedCommand(response.RequestId);
        /// Command completing asynchronously.
        cmd.Completed(response, false);
    }

    private IServer remote;
}

abstract class Command: EventArgs
{
    internal void Completed(Exception ex, bool synchronously)
    {
        this.Exception = ex;

        this.CompletedSynchronously = synchronously;

        if( this.OnCompleted != null )
        {
            this.OnCompleted(this);
        }
    }

    internal void Completed(CommandResponse response, bool synchronously)
    {
        this.Response = response;
        this.Completed(response.ExceptionFromServer, synchronously)
    }

    public bool CompletedSynchronously{ get; private set; }

    public event EventHandler<Command> OnCompleted;

    public Exception Exception{ get; private set; }

    internal protected abstract CommandRequest GetRequest();
}
+3
source share
5 answers

throwing an exception from the sending point may be useful or useful.

the calling callback passing the exception argument requires that the completion callback do two different things.

a second callback for an exception report might make sense instead

+3
source

.NET( BackgroundWorker BeginInvoke()/EndInvoke()) , . .

#:


private delegate int CommandDelegate(string number);

private void ExecuteCommandAsync()
{
    CommandDelegate del = new CommandDelegate(BeginExecuteCommand);
    del.BeginInvoke("four", new AsyncCallback(EndExecuteCommand), null);
}

private int BeginExecuteCommand(string number)
{
   if (number == "five")
   {
      return 5;
   }
   else
   {
      throw new InvalidOperationException("I only understand the number five!");
   }
}

private void EndExecuteCommand(IAsyncResult result)
{
    CommandDelegate del;
    int retVal;

    del = (CommandDelegate)((AsyncResult)result).AsyncDelegate;

    try
    {
        // Here where we get the return value
        retVal = del.EndInvoke(result);
    }
    catch (InvalidOperationException e)
    {
        // See, we had EndExecuteCommand called, but the exception
        // from the Begin method got tossed here
    }
}

, ExecuteCommandAsync(), . BeginExecuteCommand() . , , EndInvoke() IAsyncResult ( AsyncResult, , , . , " " , .

. IAsyncResult MSDN.

, .

+5

ExecuteAsync . . , . , .

+4

. , , .

+1

Handling exceptions in one place is much simpler. I would use the following difference: for exceptions to be handled, throw them in a callback. This simplifies the use of the class. For exceptions that should not be caught (for example, ArgumentException), call ExecuteAsync. We want unhandled exceptions to explode as soon as possible.

0
source

Source: https://habr.com/ru/post/1699694/


All Articles