AN UPDATED VERSION with GitHub link is avalable HERE.I am trying to define a plug and play architecture which leverages stereotypical role implementations (Validator, Reader, Writer, Logger, Query, etc) by automatically instantiating associated services through a very limited use of Service Locator.Demo code: let's say we have a query object to define some sequence of integers:class IntRange : Query<IntRange, int>{    public IntRange(int start, int count)    {        Start = start;        Count = count;    }    public int Start { get; }    public int Count { get; }}We also have couple services to be instantiated and invoked by inversion of control container. One to validate the query before execution:class RangeValidator : IValidator<IntRange>{    public Task HandleAsync(IntRange subject) =>        subject.Count < 0 || subject.Count > 10 ?            Task.FromException(new ArgumentOutOfRangeException()) :            Task.CompletedTask;}And one more for actual sequence materialization:class IntSequence : IReader<IntRange, int>{    public Task<IEnumerable<int>> HandleAsync(IntRange subject) =>        Task.FromResult(            Enumerable.Range(subject.Start, subject.Count));        }This code should magically print 10 11 12 13 14 after successful validation with matching service instantiation and execution:foreach (var i in await new IntRange(10,5))    Console.WriteLine(i);  The following snippet reports ArgumentOutOfRangeException:try{    await new IntRange(10, 100);}catch(Exception ex){    Console.WriteLine(ex);}Autofac configuration used for the demo code above:var builder = new ContainerBuilder();builder.RegisterType<IntSequence>().AsImplementedInterfaces();builder.RegisterType<RangeValidator>().AsImplementedInterfaces();var container = builder.Build();var csl = new AutofacServiceLocator(container);ServiceLocator.SetLocatorProvider(() => csl);Here are the library interfaces and classes:public interface IHandler<in TSubject, out TTask>    where TTask : Task{    TTask HandleAsync(TSubject subject);}And its specialization:public interface IValidator<in TSubject>    : IHandler<TSubject, Task>{}I have this helper class which aggregates all the registered validation services into one (insures all the matching validators to report): public class Validator<TSubject> : IValidator<TSubject>{    public Task HandleAsync(TSubject subject) =>        Task.WhenAll(            ServiceLocator.Current                .GetAllInstances<IValidator<TSubject>>()                .Select(validator => validator.HandleAsync(subject)));}    One more specialization:public interface IReader<in TQuery, TResult>    : IHandler<TQuery, Task<IEnumerable<TResult>>>{}And an aggregating helper implementation (concatenates outputs of all the compatible readers):public class Reader<TQuery, TResult> : IReader<TQuery, TResult>{    public async Task<IEnumerable<TResult>> HandleAsync(TQuery subject) =>        (await Task.WhenAll(            ServiceLocator.Current                .GetAllInstances<IReader<TQuery, TResult>>()                .Select(reader => reader.HandleAsync(subject))))        .SelectMany(results => results);}Now the base class for the query to define validation, reading, and caching strategy:public abstract class Query<TQuery, TResult>    where TQuery : Query<TQuery, TResult>{    protected Query()    {        Data = new Lazy<Task<IEnumerable<TResult>>>(ExecuteAsync);    }    public TaskAwaiter<IEnumerable<TResult>> GetAwaiter() =>        Data.Value.GetAwaiter();    Lazy<Task<IEnumerable<TResult>>> Data { get; }    protected virtual async Task<IEnumerable<TResult>> ExecuteAsync()    {        await new Validator<TQuery>()            .HandleAsync((TQuery)this);        return await new Reader<TQuery, TResult>()            .HandleAsync((TQuery)this);                }}PROS:Reusability (minimum amount of code for query invocation)Extensibility (easy to add more handling services)Traceability (ExecuteAsync defines a central point to add logging)Query objects are serializable/model-bindable DTORequired dependencies are injectable to handling services by IoC container.Testable through LocatorService mocking (empty locator provides a reasonable default behavior).CONS:It is a ServiceLocator.
Injecting stereotypical role implementations by IoC container
c#;design patterns