Skip to content

Instantly share code, notes, and snippets.

@fabiomaulo
Last active October 2, 2018 11:18
Show Gist options
  • Save fabiomaulo/d4d9e06311b0eef45f8f2f14f7c7fcb0 to your computer and use it in GitHub Desktop.
Save fabiomaulo/d4d9e06311b0eef45f8f2f14f7c7fcb0 to your computer and use it in GitHub Desktop.
The Meditor framework
/// <summary>
/// Define an object that encapsulates how a set of objects interact.
/// Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.
/// </summary>
public interface IMediator
{
/// <summary>
/// Perform a query to the "model".
/// </summary>
/// <typeparam name="TRequest">The type of the request.</typeparam>
/// <typeparam name="TResult">The type of the result of the query.</typeparam>
/// <param name="request">The request or the criteria of the query.</param>
/// <param name="cancellationToken">The cancelation token.</param>
/// <returns>The result of the query.</returns>
Task<TResult> Query<TRequest, TResult>(TRequest request, CancellationToken cancellationToken = default(CancellationToken));
}
/// <summary>
/// Real mediator between the two types.
/// </summary>
/// <typeparam name="TRequest">Original Type.</typeparam>
/// <typeparam name="TResult">Result Type.</typeparam>
public interface IMediator<in TRequest, TResult>
{
/// <summary>
/// Handle the madiation between the two types.
/// </summary>
/// <param name="request">The original instance of the request to handle.</param>
/// <param name="prevResult">The possible previous result when a mediator is executed in a pipeline.</param>
/// <param name="cancellationToken">The cancelation token.</param>
/// <returns>The result of the mediation.</returns>
Task<TResult> Handle(TRequest request, TResult prevResult = default(TResult), CancellationToken cancellationToken = default(CancellationToken));
}
public class Mediator : IMediator
{
private readonly Func<Type, object> mediatorFactory;
public Mediator(Func<Type, object> mediatorFactory)
{
this.mediatorFactory = mediatorFactory ?? throw new ArgumentNullException(nameof(mediatorFactory));
}
public Task<TResult> Query<TRequest, TResult>(TRequest request, CancellationToken cancellationToken = default(CancellationToken))
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
var mediatorType = typeof(IMediator<,>).MakeGenericType(typeof(TRequest), typeof(TResult));
var concreteMediator = (IMediator<TRequest, TResult>)mediatorFactory(mediatorType);
return concreteMediator.Handle(request, cancellationToken: cancellationToken);
}
}
public class PipelineMediator<TRequest, TResult>: IMediator<TRequest, TResult>
{
private readonly IMediator<TRequest, TResult> mediator;
public PipelineMediator(IMediator<TRequest, TResult> mediator
, IMediator<TRequest, TResult> innerMediator = null)
{
this.mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
InnerMediator = innerMediator ?? new NoOpMediator<TRequest, TResult>();
}
protected IMediator<TRequest, TResult> InnerMediator { get; }
public async Task<TResult> Handle(TRequest request, TResult prevResult = default(TResult), CancellationToken cancellationToken = default(CancellationToken))
{
var r = await mediator.Handle(request, prevResult, cancellationToken);
var rr = await InnerMediator.Handle(request, r, cancellationToken);
return rr;
}
}
internal class NoOpMediator<TRequest, TResult> : IMediator<TRequest, TResult>
{
public Task<TResult> Handle(TRequest request, TResult prevResult = default(TResult), CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(prevResult);
}
}
public static class MediatorPipelineCreator
{
public static IMediator<TRequest, TResult> Then<TRequest, TResult>(this IMediator<TRequest, TResult> outer
, IMediator<TRequest, TResult> inner)
{
return new PipelineMediator<TRequest, TResult>(outer ?? throw new ArgumentNullException(nameof(outer), "Invalid mediator pipeline.")
, inner ?? throw new ArgumentNullException(nameof(inner), "Invalid mediator pipeline."));
}
}
@fabiomaulo
Copy link
Author

Usage example

Real world

	public class PizzaController
	{
		private readonly IMediator mediator;

		public PizzaController(IMediator mediator)
		{
			this.mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
		}

		public async Task DoSomethingWith(int request)
		{
			double model = await mediator.Query<int, double>(request);
			Console.WriteLine(model);
		}
	}

	public class ToHalfMediator : IMediator<int, double>
	{
		public Task<double> Handle(int request, double prevResult = 0D, CancellationToken cancellationToken = default(CancellationToken))
		{
                        // check working in pipeline
			var result = prevResult == default(double) 
				? request / 2D
				: prevResult / 2D;
			return Task.FromResult(result);
		}
	}

Registration

	class Program
	{
		private static readonly MinimalIoCContainer container = new MinimalIoCContainer();

		static void Main(string[] args)
		{
			container.RegisterAllServices();
			CallAction(10);
			CallAction(15);
			CallAction(12);
			Console.ReadLine();
		}

		private static void CallAction(int value)
		{
			var controller = container.GetInstance<PizzaController>();
			controller.DoSomethingWith(value).Wait();
		}
	}

	public static class RegisterServices
	{
		public static IDepencencyInjectionStore RegisterAllServices(this IDepencencyInjectionStore store)
		{
			store.RegisterSingleton<IMediator>(c => new Mediator(t=> c.GetInstance(t)));
			store.RegisterSingleton<IMediator<int, double>>(c => 
				new ToHalfMediator().Then(new ToHalfMediator())
			);

			store.RegisterTransient(c => new PizzaController(c.GetInstance<IMediator>()));
			return store;
		}
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment