Last active
February 18, 2023 21:45
-
-
Save ar3cka/ed2217ee17f6bd3f2a91 to your computer and use it in GitHub Desktop.
CQRS. In Process Command Dispatcher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Runtime.Remoting.Messaging; | |
using System.Runtime.Remoting.Proxies; | |
using System.Threading.Tasks; | |
namespace Demo | |
{ | |
#region Command Interfaces | |
public interface ICommand | |
{ | |
} | |
public interface ICommandHandler<in TCommand, TCommandResult> where TCommand : ICommand | |
{ | |
Task<TCommandResult> ExecuteAsync(TCommand command); | |
} | |
#endregion | |
#region Command Dispatching | |
public interface ICommandDispatcher | |
{ | |
Task<object> Dispatch(ICommand command); | |
} | |
public class CommandDispatcher : ICommandDispatcher | |
{ | |
private readonly IDictionary<Type, object> _handlers; | |
public CommandDispatcher(IDictionary<Type, object> handlers) | |
{ | |
_handlers = handlers; | |
} | |
public async Task<object> Dispatch(ICommand command) | |
{ | |
var handler = (dynamic)_handlers[command.GetType()]; | |
return await handler.ExecuteAsync((dynamic)command); | |
} | |
} | |
public class DispatcherFactory | |
{ | |
public ICommandDispatcher Create(Endpoint endpoint) | |
{ | |
var handlers = AppDomain.CurrentDomain.GetAssemblies() | |
.SelectMany(assembly => assembly.GetExportedTypes()) | |
.Where(type => !type.IsInterface) | |
.Where(type => type.GetInterfaces().Any(interfaceType => interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(ICommandHandler<,>))) | |
.ToDictionary( | |
type => type.GetMethod("ExecuteAsync", BindingFlags.Instance | BindingFlags.Public).GetParameters()[0].ParameterType, | |
Activator.CreateInstance); | |
return new CommandDispatcher(handlers); | |
} | |
} | |
public class Endpoint | |
{ | |
public static Endpoint CreateInProcessEndpoint() | |
{ | |
return new InProcessEndpoint(); | |
} | |
} | |
public class InProcessEndpoint : Endpoint | |
{ | |
} | |
#endregion | |
#region Proxy Factoring | |
public class ProxyFactory | |
{ | |
public T Create<T>() | |
{ | |
return Create<T>(Endpoint.CreateInProcessEndpoint()); | |
} | |
public T Create<T>(Endpoint endpoint) | |
{ | |
var factory = new DispatcherFactory(); | |
var dispatcher = factory.Create(endpoint); | |
var proxy = new ServiceProxy<T>(dispatcher); | |
return (T)proxy.GetTransparentProxy(); | |
} | |
} | |
public class ServiceProxy<T> : RealProxy | |
{ | |
public ServiceProxy(ICommandDispatcher dispatcher) | |
: base(typeof(T)) | |
{ | |
_dispatcher = dispatcher; | |
} | |
public override IMessage Invoke(IMessage msg) | |
{ | |
var methodCall = msg as IMethodCallMessage; | |
var command = methodCall.Args[0] as ICommand; | |
var methodType = methodCall.MethodBase as MethodInfo; | |
var resultType = methodType.ReturnType.GetGenericArguments()[0]; | |
var invokeCommand = CommandInvokerMethods.GetCommandInvocationMethod(resultType); | |
var result = invokeCommand.Invoke( | |
null, | |
new object[] { _dispatcher, command }); | |
return new ReturnMessage( | |
result, | |
null, | |
0, | |
methodCall.LogicalCallContext, | |
methodCall); | |
} | |
private readonly ICommandDispatcher _dispatcher; | |
} | |
public class CommandInvokerMethods | |
{ | |
public static MethodInfo GetCommandInvocationMethod(Type resultType) | |
{ | |
var invokeCommandMethod = typeof(CommandInvokerMethods).GetMethod("InvokeCommand", BindingFlags.Static | BindingFlags.Public); | |
var invokeCommand = invokeCommandMethod.MakeGenericMethod(resultType); | |
return invokeCommand; | |
} | |
public static async Task<TResult> InvokeCommand<TResult>(ICommandDispatcher dispatcher, ICommand command) | |
{ | |
return (TResult)await dispatcher.Dispatch(command); | |
} | |
} | |
#endregion | |
#region Sample Service | |
public class DoWork : ICommand | |
{ | |
} | |
public class DoWorkCommand : ICommandHandler<DoWork, int> | |
{ | |
public Task<int> ExecuteAsync(DoWork command) | |
{ | |
return Task.FromResult(new Random().Next()); | |
} | |
} | |
public interface IWorkerService : ICommandHandler<DoWork, int> | |
{ | |
} | |
#endregion | |
internal class Program | |
{ | |
private static void Main(string[] args) | |
{ | |
var factory = new ProxyFactory(); | |
var proxy = factory.Create<IWorkerService>(); | |
Console.WriteLine(proxy.ExecuteAsync(new DoWork()).Result); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment