Search code examples
c#servicestackcastle-windsorioc-containerlifetime-scoping

ServiceStack self-hosted application with per-request lifetime scope


Working with ServiceStack I've stuck with the problem of objects lifetime management in self-hosted web application.

My requirements:

  1. Need of per-request objects lifetime scope.
  2. I'm using Castle Windsor IoC with implemented ServiceStack IoC adapter.
  3. My application is self-hosted with base class AppHostHttpListenerPoolBase (ServiceStack v4)
  4. Probably one day I want to move on IIS, thus it's must be flexible.

General problem:

Castle Windsor IoC implements its own per-request lifetime strategy but it is binded to http modules, thus it works only with IIS hosted apps. Hence, I have to implement my custom IScopeAccessor (provided by Castle Windsor) to handle objects lifetime. The problem here is in absence of hooks which I can use to bind to current request.

Given

public class MyScopeAccessor : IScopeAccessor
{
    public ILifetimeScope GetScope(CreationContext context)
    {
       //implement it
    }
}

I have to implement GetScope method.

There are two main ideas I cannot complete:

Using of [Threadstatic]

In MyScopeAccessor I just store

[ThreadStatic] 
private static ILifetimeScope _currentLifetimeScope;

and create new scope after first GetScope if it's not initialzied yet.

Problems:

  1. Hard to dispose. Best way to dispose _currentLifetimeScope is to implement custom IServiceRunner (or inherit from ServiceRunner) overriding AfterEachRequest method. But I don't exactly know if AfterEachRequest is actually executed in request thread.
  2. Moving to IIS can cause some problems because as I know IIS doesn't guarantee unchangeable binding between theads and request contexts.

Using of IRequest instance

In MyScopeAccessor I just store

private static readonly ConcurrentDictionary<IRequest, ILifetimeScope> LifetimeScopes;

and create and dispose current lifetime scope in corresponding custom ServiceRunner methods (OnBeforeEachRequest, OnAfterEachRequest).

Problems:

  1. I don't know how to get access to current request globally from GetScope, MyScopeAccessor knows nothing about services and requests.

Also, it is interesting if ServiceStack default Funq IoC solves this problem.


Solution

  • Funq does handle RequestScoped dependencies which stores Request Context dependencies in the RequestContext.Instance.Items[] dictionary.

    Any disposables can be registered in RequestContext.Instance.TrackDisposable() are automatically disposed of at the end of the request.

    At the end of each request AppHost.OnEndRequest() is fired which goes through and releases any dependencies stored in the RequestContext for that request.

    If your Windsor ContainerAdapter implements the IRelease interface it's automatically called to release any instances which can be handled itself. Both these API's are overridable in your AppHost if you want to change the default behavior:

    public virtual void OnEndRequest()
    {
        var disposables = RequestContext.Instance.Items.Values;
        foreach (var item in disposables)
        {
            Release(item);
        }
    
        RequestContext.Instance.EndRequest();
    }
    
    public virtual void Release(object instance)
    {
        try
        {
            var iocAdapterReleases = Container.Adapter as IRelease;
            if (iocAdapterReleases != null)
            {
                iocAdapterReleases.Release(instance);
            }
            else
            {
                var disposable = instance as IDisposable;
                if (disposable != null)
                    disposable.Dispose();
            }
        }
        catch { /*ignore*/ }
    }