Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Command pattern and asynchronous operations handling in C#

I'd like to hear opinions on the best way to handle asynchronous operations with the Command pattern. Say we have the following example:

public class MyCommand 
{
   // Sets up receiver and does whatever stuff

   public void Execute()
   {  
       _myReceiver.DoSomething();
   } 
}

The problem is: MyCommand doesn't know whether MyReceiver.DoSomething() has async portions of code. If i wanted to push MyCommand into an undo stack after its execution, i couldn't guarantee that its receiver action has been fully executed, making it uncertain to know if MyCommand reached a state where undoing is possible or not.

I personally thought on the following solution:

  1. Implement some sort of state control in Command
  2. Include "BeginExecute" and "EndExecute" in Command
  3. Include events in MyReceiver and make Command subscribe to them (that seems smelly to me)

To wrap things up, MyCommand would turn into:

public class MyCommand 
{
   public MyCommand(MyReceiver receiver)
   {   
      _myReceiver = receiver;
      _myReceiver.DoSomethingFinished += () => this.EndExecute(); 
   }

   public void BeginExecute()
   {  
       this.EnterExecutionState();

       _myReceiver.DoSomething();
   } 

   public void EndExecute()
   {  
       this.LeaveExecutionState();
   } 

   // State handling related stuff
}

I now have the means to make sure the Command's receiver has finished executing whatever action and it's ready to be pushed into the undo stack. However, to event-spam every single Receiver class that contains async operations really bugs me.

I haven't found much about this topic in the Internet and would love to hear different approaches.

OBS: Make the Command manage all the asynchronous-related code isn't an option :).

like image 254
ferspanghero Avatar asked Jan 05 '12 21:01

ferspanghero


People also ask

What is the command pattern used for?

In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.

What is command pattern in C#?

The command pattern is a behavioral design pattern. This pattern encapsulates a request as an object that contains all the information about the request, including requests for queues or logs, allowing for much more complex architectures. It even allows operations like undo and redo.

What is asynchronous call in C#?

C# has a language-level asynchronous programming model, which allows for easily writing asynchronous code without having to juggle callbacks or conform to a library that supports asynchrony. It follows what is known as the Task-based Asynchronous Pattern (TAP).

What is command design pattern C++?

Command in C++ Command is behavioral design pattern that converts requests or simple operations into objects. The conversion allows deferred or remote execution of commands, storing command history, etc.


2 Answers

I think you've got way too much going on in a single class. I would break it down like this:

// An immutable command, to be handled in-process.  
// ICommand is a marker interface with no members.
public class DoSomething : ICommand 
{
    public readonly Id;

    public DoSomething(Guid id)
    {
        Id = id;
    }
}

// To be handled out-of-process.
[AsynchronousCommand]
public class DoSomethingThatTakesAReallyLongTime : ICommand
{
    public readonly Id;

    public DoSomethingThatTakesAReallyLongTime(Guid id)
    {
        Id = id;
    }
}

// This guy could take any number of dependencies: ISomethingRepository, DbContext, etc.
// Doesn't matter, but it's probably gonna have dependencies.
public class DoSomethingHandler : IHandler<DoSomething> 
{
    public void Handle(DoSomething command) // IHandler<T>'s only member
    {
        // CRUD or call call a domain method
    }
}

public class CommandService : ICommandService
{
    public void Execute(params ICommand[] commands) // ICommandService's only member
    { 
        foreach(var command in commands)
        {
            var handler = GetHandler(command); // Could use your IOC container.

            if (HasAsyncAttribute())
                new Action(() => handler.Handle(command)).BeginInvoke(null, null);
            else
                handler.Handle(command);
        }
    } 
}

// Something that might consume these
public class SomethingController
{
    private readonly ICommandService _commandService;

    public SomethingController(ICommandService commandService)
    {
        _commandService = commandService;
    }

    [HttpPost]
    public void DoSomething(Guid id)
    {
        _commandService.Execute(new DoSomething(id));
    }

    [HttpPost]
    public void DoSomethingThatTakesAReallyLongTime(Guid id)
    {
        _commandService.Execute(new DoSomethingThatTakesAReallyLongTime(id));
    }
}

The big advantage here is that you can distribute your commands to clients without explicitly dragging along all the dependencies that go with the handlers. The handlers should not be known to the client. All the client needs to know is that it sent a command, and all commands should be assumed to succeed.

like image 189
Josh Kodroff Avatar answered Sep 23 '22 14:09

Josh Kodroff


Something like this?

public interface ICommand
{
    void Execute();
    event EventHandler Finished;
}

public class MyCommand : ICommand
{
   public MyCommand(MyReceiver receiver)
   {   
      _myReceiver = receiver;
      _myReceiver.DoSomethingFinished += () => Finished(); // dont forget null check here.
   }

   public void Execute()
   {      
       _myReceiver.DoSomething();
   } 

   public event EventHandler Finished;
}

This way, user of this command can register to Finished event so it knows when command has finished its async behaviour and can act acordingly.

Or if you dont wan't to use event, then what about callback?

public class MyCommand : ICommand
{
   public MyCommand(MyReceiver receiver)
   {   
      _myReceiver = receiver;
   }

   public void Execute()
   {      
       _myReceiver.DoSomething(() => Finished()); // dont forget null check here.
   } 

   public event EventHandler Finished;
}

Either way, there simply need to be a way for MyReciever to notify its caller, that it finished. There is no way to bypass it.

like image 24
Euphoric Avatar answered Sep 21 '22 14:09

Euphoric