danielwertheim

danielwertheim


notes from a passionate developer

Developer that lives by the mantra "code is meant to be shared".

Share


Tags


Disclaimer

This is a personal blog. The opinions expressed here represent my own and not those of my employer, nor current or previous. All content is published "as is", without warranty of any kind and I don't take any responsibility and can't be liable for any claims, damages or other liabilities that might be caused by the content.

Routemeister and Autofac

Daniel WertheimDaniel Wertheim

With the release of v0.3.0 the internal routing as well as the strategy for creating handlers got access to a MessageEnvelope. The envelope carries the message and custom state. This custom state can with middlewares be used to e.g. assign a certain Autofac child lifetime scope, that later on is used when resolving the handlers. When the message has been routed, you dispose the scope in the middleware. Thereby you get per-request scoped handlers.

Before continuing, it might be worth to read the short post about "Routemeister and middlewares"

Code sample

Full code sample exists in this gist.

Why

Well, by not doing this, the handlers that are created, will not get disposed until the context goes out of scope. If you are unlucky, this can be the root container itself. Then what happens if the message is routed to a handler that has a dependency on e.g. a TCP connection? That will set there... waiting...

The Scene

Lets say we have a simple handler ConcreteHandler that handles a simple message MyMessage. The handler also has a dependency on SomeDependency, which we want resolved by the IoC-container, in this case Autofac. SomeDependency implements IDisposable so that we can see that it will be released. This gives us something like this:

public class MyMessage { }

public class ConcreteHandler :  
    ISomeMessageHandler<MyMessage>,
    IDisposable
{
    private readonly SomeDependency _dependency;

    public ConcreteHandler(SomeDependency dependency)
    {
        _dependency = dependency;
    }

    public async Task HandleAsync(MyMessage message)
    {
        await _dependency.DoWorkAsync();
    }

    public void Dispose()
    {
        Console.WriteLine("I am being released");
    }
}

public class SomeDependency : IDisposable  
{
    public Task DoWorkAsync()
    {
        Console.WriteLine("Doing work with underlying connection");
        return Task.FromResult(0);
    }

    public void Dispose()
    {
        Console.WriteLine("I'm cleaning the underlying connection.");
    }
}

Let's just get a consuming ConsoleApplication, that will route two messages. Hence we expect the handler and SomeDependency to be instantiated twice.

class Program  
{
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterAssemblyModules(typeof(Program).Assembly);
        var container = builder.Build();

        var router = container.Resolve<IAsyncMessageRouter>();
        router.RouteAsync(new MyMessage()).Wait();
        router.RouteAsync(new MyMessage()).Wait();
    }
}

Bootstrap the Autofac module

I'm making the assumption that you have Autofac bootstraped and all we do know is registering a Autofac module, with Routemeister.

public class OurModule : Module  
{
    protected override void Load(ContainerBuilder builder)
    {
        //This is where we will add stuff
    }
}

First lets just register the handler and the dependency. We do this manually now:

builder.RegisterType<SomeDependency>();  
builder.RegisterType<ConcreteHandler>();  

Next step. Register the routes:

//MessageRoutes
builder.Register(ctx =>  
{
    var factory = new MessageRouteFactory();

    return factory.Create(
        typeof(Program).Assembly,
        typeof(ISomeMessageHandler<>));
})
.SingleInstance();

Next step. Register the router:

//Router
builder.Register(ctx =>  
{
    var routes = ctx.Resolve<MessageRoutes>();
    var parentScope = ctx.Resolve<ILifetimeScope>();
    var router = new MiddlewareEnabledAsyncMessageRouter(
        (type, envelope) => envelope.GetScope().Resolve(type),
        routes);
    router.Use(next => envelope =>
    {
        using (var childScope = parentScope.BeginLifetimeScope())
        {
            envelope.SetScope(childScope);
            return next(envelope);
        }
    });

    return router;
})
.As<IAsyncMessageRouter>()
.SingleInstance();

The trick here is to make use of the router.Use method. It lets you intercept routing calls. And the MessageEnvelope argument it gets, contains the message and also a state bag to which you can assign state:

envelope.SetState("someKey", someValue);  

To simplify this, I made an extension method:

internal static class MessageEnvelopeExtensions  
{
    internal static void SetScope(
        this MessageEnvelope envelope, ILifetimeScope scope)
    {
        envelope.SetState("scope", scope);
    }

    internal static ILifetimeScope GetScope(
        this MessageEnvelope envelope)
    {
        return envelope.GetState("scope") as ILifetimeScope;
    }
}

We use this to create a "child scope" in Autofac, in a using block and inside it, we let the routing flow continue by invoking next(envelope):

router.Use(next => envelope =>  
{
    using (var childScope = parentScope.BeginLifetimeScope())
    {
        envelope.SetScope(childScope);
        return next(envelope);
    }
});

Now lets run the app:

That's it. It looks extensive. But look at the whole code (gist here) and you will see that it's not that bad. At least doable. And something you do once.

Cheers,

//Daniel

Developer that lives by the mantra "code is meant to be shared".

Comments