Skip to content

Instantly share code, notes, and snippets.

@dbones
Created February 11, 2017 23:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dbones/ead404584c326c0be22c87111e833e35 to your computer and use it in GitHub Desktop.
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)
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