Created
February 11, 2017 23:03
-
-
Save dbones/ead404584c326c0be22c87111e833e35 to your computer and use it in GitHub Desktop.
middleware class, which functions like the middleware you will find in .NET Core, Express (on node.js)
This file contains 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
namespace MiddleWare.Host | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Threading.Tasks; | |
using Autofac; | |
internal class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
//setup the container | |
var builder = new ContainerBuilder(); | |
builder.RegisterType<MiddleWareAction>().InstancePerLifetimeScope(); | |
builder.RegisterType<RandomWait>().SingleInstance(); | |
using (var container = builder.Build()) | |
{ | |
//setup the app / which is based on middleware | |
//regsiter middleware with the Use and Add functions | |
//the order will be the order actions are executed in | |
var app = new Middleware<string>(new AutofacResolver(container)); | |
app.Use(async (ctx, next) => | |
{ | |
var stopwatch = new Stopwatch(); | |
stopwatch.Start(); | |
await next(ctx); | |
stopwatch.Stop(); | |
Console.WriteLine(stopwatch.ElapsedTicks); | |
}); | |
app.Add<MiddleWareAction>(); | |
//app.Add<RandomWait>(); | |
app.Use((ctx, next) => | |
{ | |
Console.WriteLine(ctx); | |
return Task.CompletedTask; | |
}); | |
//app is now running. | |
var task1 = app.Execute("hello world"); | |
var task2 = app.Execute("foo bar"); | |
Task.WaitAll(task1, task2); | |
Console.WriteLine("complete"); | |
} | |
} | |
} | |
public interface IResolverBase : IDisposable | |
{ | |
object Resolve(Type type); | |
} | |
public interface IResolver : IResolverBase | |
{ | |
IScopedResolver BeginScope(); | |
} | |
public interface IScopedResolver : IResolverBase | |
{ | |
} | |
public class AutofacScopedResolver : IScopedResolver | |
{ | |
private readonly ILifetimeScope _scope; | |
public AutofacScopedResolver(ILifetimeScope scope) | |
{ | |
_scope = scope; | |
} | |
public void Dispose() | |
{ | |
_scope.Dispose(); | |
} | |
public object Resolve(Type type) | |
{ | |
return _scope.Resolve(type); | |
} | |
} | |
public class AutofacResolver : IResolver | |
{ | |
private readonly IContainer _container; | |
public AutofacResolver(IContainer container) | |
{ | |
_container = container; | |
} | |
public void Dispose() | |
{ | |
_container.Dispose(); | |
} | |
public object Resolve(Type type) | |
{ | |
return _container.Resolve(type); | |
} | |
public IScopedResolver BeginScope() | |
{ | |
return new AutofacScopedResolver(_container.BeginLifetimeScope()); | |
} | |
} | |
public class Middleware<TContext> | |
{ | |
private readonly IResolver _resolver; | |
private readonly List<PipeItem> _pipedTypes = new List<PipeItem>(); | |
public Middleware(IResolver resolver) | |
{ | |
_resolver = resolver; | |
} | |
public void Add<T>() where T : IAction<TContext> | |
{ | |
_pipedTypes.Add(new PipeItem(typeof(T))); | |
} | |
public void Use(Func<TContext, Next<TContext>, Task> action) | |
{ | |
_pipedTypes.Add(new PipeItem(new ActionWrapper<TContext>(action))); | |
} | |
public async Task Execute(TContext context) | |
{ | |
var enumerator = _pipedTypes.GetEnumerator(); | |
using (var scope = _resolver.BeginScope()) | |
{ | |
var factory = new GetNextFactory<TContext>(enumerator, scope); | |
await factory.GetNext()(context); | |
} | |
} | |
} | |
internal class GetNextFactory<TContext> | |
{ | |
private readonly IEnumerator<PipeItem> _enumerator; | |
private readonly IScopedResolver _scope; | |
public GetNextFactory(IEnumerator<PipeItem> enumerator, IScopedResolver scope) | |
{ | |
_enumerator = enumerator; | |
_scope = scope; | |
} | |
public Next<TContext> GetNext() | |
{ | |
if (!_enumerator.MoveNext()) | |
{ | |
return ctx => Task.CompletedTask; | |
} | |
var pipedType = _enumerator.Current; | |
Next<TContext> result = ctx => | |
{ | |
IAction<TContext> middleware = null; | |
if (pipedType.PipeItemType == PipeItemType.Type) | |
{ | |
middleware = (IAction<TContext>) _scope.Resolve(pipedType.Type); | |
} | |
else | |
{ | |
middleware = (IAction<TContext>) pipedType.GivenInstance; | |
} | |
return middleware.Execute(ctx, GetNext()); | |
}; | |
return result; | |
} | |
} | |
public class ActionWrapper<T> : IAction<T> | |
{ | |
private readonly Func<T, Next<T>, Task> _action; | |
public ActionWrapper(Func<T, Next<T>, Task> action) | |
{ | |
_action = action; | |
} | |
public async Task Execute(T context, Next<T> next) | |
{ | |
await _action(context, next); | |
} | |
} | |
internal class PipeItem | |
{ | |
public PipeItem(Type type) | |
{ | |
Type = type; | |
PipeItemType = PipeItemType.Type; | |
} | |
public PipeItem(object givenInstance) | |
{ | |
GivenInstance = givenInstance; | |
PipeItemType = PipeItemType.Instance; | |
} | |
public Type Type { get; private set; } | |
public object GivenInstance { get; private set; } | |
public PipeItemType PipeItemType { get; private set; } | |
} | |
public enum PipeItemType | |
{ | |
Type, | |
Instance | |
} | |
public interface IAction<T> | |
{ | |
Task Execute(T context, Next<T> next); | |
} | |
public class RandomWait : IAction<string> | |
{ | |
readonly Random _random = new Random(); | |
public async Task Execute(string context, Next<string> next) | |
{ | |
var mseconds= _random.Next(3, 10) * 1000; | |
System.Threading.Thread.Sleep(mseconds); | |
await next(context); | |
} | |
} | |
public class MiddleWareAction : IAction<string> | |
{ | |
public async Task Execute(string context, Next<string> next) | |
{ | |
Console.WriteLine($"before {context}"); | |
await next(context); | |
Console.WriteLine($"after {context}"); | |
} | |
} | |
public delegate Task Next<in T>(T context); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment