Skip to content

Instantly share code, notes, and snippets.

@ar3cka
Last active February 18, 2023 21:45
Show Gist options
  • Save ar3cka/ed2217ee17f6bd3f2a91 to your computer and use it in GitHub Desktop.
Save ar3cka/ed2217ee17f6bd3f2a91 to your computer and use it in GitHub Desktop.
CQRS. In Process Command Dispatcher
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