Skip to content

Instantly share code, notes, and snippets.

@lars-erik
Created February 13, 2019 21:26
Show Gist options
  • Save lars-erik/d3359a6d0425a8b30dfe4e853733a411 to your computer and use it in GitHub Desktop.
Save lars-erik/d3359a6d0425a8b30dfe4e853733a411 to your computer and use it in GitHub Desktop.
Simple but flexible command handling
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace SimpleCommands.Tests
{
[TestFixture]
public class When_Processing_A_Command_With_A_Composite
{
[Test]
public void The_Outer_One_Aggregates_Results()
{
var command = new SpecificCommand("Some data");
var handler = new CompositeHandler<SpecificCommand>(
new EchoTask(),
new EchoTask()
);
var result = handler.Execute(command);
Assert.That(result,
Is.InstanceOf<AggregateCommandResult>() &
Has.Property("Results").With.Exactly(2).With.Property("Data").EqualTo("Some data echoed")
);
}
[Test]
public void The_Composite_Can_Log_Exceptions_And_Cancel_Processing()
{
var command = new SpecificCommand("Some data");
var handler = new CompositeHandler<SpecificCommand>(
new EchoTask(),
new ErrorTask(),
new EchoTask()
);
var result = handler.Execute(command);
Assert.That(result,
Is.InstanceOf<AggregateCommandResult>() &
Has.Property("Results").With.One.InstanceOf<EchoResult>().With.Property("Data").EqualTo("Some data echoed") &
Has.Property("Results").With.One.InstanceOf<ErrorResult>()
);
}
}
public class SpecificCommand : Command
{
public string Data { get; set; }
public SpecificCommand()
{
}
public SpecificCommand(string data)
{
Data = data;
}
}
public class EchoTask : CommandHandler<SpecificCommand>
{
public override CommandResult Execute(SpecificCommand command)
{
return new EchoResult(command.Data + " echoed");
}
}
public class ErrorTask : CommandHandler<SpecificCommand>
{
public override CommandResult Execute(SpecificCommand command)
{
throw new NotImplementedException();
}
}
public class EchoResult : CommandResult
{
public string Data { get; }
public EchoResult(string data)
{
Data = data;
}
}
public class CompositeHandler<T> : CommandHandler<T>
where T : Command
{
private readonly List<ICommandHandler<T>> handlers;
public CompositeHandler(params ICommandHandler<T>[] handlers)
{
this.handlers = handlers.ToList();
}
public override CommandResult Execute(T command)
{
return handlers.Aggregate(
new AggregateCommandResult(),
(r, h) => Execute(command, r, h)
);
}
private static AggregateCommandResult Execute(T command, AggregateCommandResult result, ICommandHandler<T> handler)
{
if (result.Results.OfType<ErrorResult>().Any())
return result;
return result.Add(TryExecute(command, handler));
}
private static CommandResult TryExecute(T command, ICommandHandler<T> handler)
{
CommandResult commandResult;
try
{
commandResult = handler.Execute(command);
}
catch
{
commandResult = new ErrorResult();
}
return commandResult;
}
}
public class AggregateCommandResult : CommandResult
{
private readonly List<CommandResult> results = new List<CommandResult>();
public IEnumerable<CommandResult> Results => results;
public AggregateCommandResult Add(CommandResult result)
{
results.Add(result);
return this;
}
}
public abstract class CommandHandler<T> : ICommandHandler<T>
where T : Command
{
public CommandResult Execute(Command command)
{
return Execute((T) command);
}
public abstract CommandResult Execute(T command);
}
public interface ICommandHandler<T> : ICommandHandler
where T : Command
{
CommandResult Execute(T command);
}
public interface ICommandHandler
{
CommandResult Execute(Command command);
}
public class Command
{
}
public class CommandResult
{
}
public class ErrorResult : CommandResult
{
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment