Skip to content

Instantly share code, notes, and snippets.

@IanYates
Created January 20, 2016 23:30
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 IanYates/29180ce09c41e42b9a7e to your computer and use it in GitHub Desktop.
Save IanYates/29180ce09c41e42b9a7e to your computer and use it in GitHub Desktop.
Proper integration between Hangfire and Windsor - does not take advantage of new HangFire 1.5 IoC features
To configure Hangfire
//Windsor integration
//The activator
var jobActivator = new WindsorScopedJobActivator(windsorContainer);
GlobalConfiguration.Configuration.UseActivator(jobActivator);
//And something to create and dispose of a child container in the activator for each job
var windsorJobFilter = new WindsorContainerPerJobFilterAttribute(jobActivator);
GlobalJobFilters.Filters.Add(windsorJobFilter);
public class WindsorScopedJobActivator : JobActivator
{
private readonly IWindsorContainer parentContainer;
private readonly ThreadLocal<IWindsorContainer> childContainer = new ThreadLocal<IWindsorContainer>(trackAllValues:false);
public WindsorScopedJobActivator(IWindsorContainer parentContainer)
{
this.parentContainer = parentContainer;
}
public override object ActivateJob(Type jobType)
{
return childContainer.Value.Resolve(jobType);
}
private void DisposeLocalThreadContainer()
{
if (!childContainer.IsValueCreated) return;
var c = childContainer.Value;
if (c == null) return;
childContainer.Value = null;
c.Dispose();
}
public void StartJobLifetimeScope()
{
//Defensive
DisposeLocalThreadContainer();
childContainer.Value = new WindsorContainer(
//Specific name on container - avoids race conditions when calculating
//http://nguyducthuan.com/blog/2015/01/15/Race-condition-bug-of-Windsor-Castle-child-container-3-x/
Guid.NewGuid().ToString(),
new DefaultKernel(),
new DefaultComponentInstaller()
) { Parent = parentContainer };
}
public void EndJobLifetimeScope()
{
DisposeLocalThreadContainer();
}
}
/// <summary>
/// This class runs on the same thread as the job that will be performed by HangFire
/// So we can use it to create a Windsor container instance on the thread and then dispose of it
/// This lets us properly manage transient resources :)
/// </summary>
public class WindsorContainerPerJobFilterAttribute : JobFilterAttribute, IServerFilter
{
private readonly WindsorScopedJobActivator windsorPerLifetimeScopeJobActivator;
public WindsorContainerPerJobFilterAttribute(WindsorScopedJobActivator windsorPerLifetimeScopeJobActivator)
{
this.windsorPerLifetimeScopeJobActivator = windsorPerLifetimeScopeJobActivator;
}
public void OnPerforming(PerformingContext filterContext)
{
windsorPerLifetimeScopeJobActivator.StartJobLifetimeScope();
}
public void OnPerformed(PerformedContext filterContext)
{
windsorPerLifetimeScopeJobActivator.EndJobLifetimeScope();
}
}
@hikalkan
Copy link

Thank you very much.

What about this implementation which uses JobActivatorScope instead of filter? It seems more natural for Hangfire.

    public class HangfireIocJobActivator : JobActivator
    {
        private readonly IIocResolver _iocResolver;

        public HangfireIocJobActivator(IIocResolver iocResolver)
        {
            if (iocResolver == null)
            {
                throw new ArgumentNullException("iocResolver");
            }

            _iocResolver = iocResolver;
        }

        public override object ActivateJob(Type jobType)
        {
            return _iocResolver.Resolve(jobType);
        }

        public override JobActivatorScope BeginScope()
        {
            return new HangfireIocJobActivatorScope(this, _iocResolver);
        }

        class HangfireIocJobActivatorScope : JobActivatorScope
        {
            private readonly JobActivator _activator;
            private readonly IIocResolver _iocResolver;

            private readonly List<object> _resolvedObjects;

            public HangfireIocJobActivatorScope(JobActivator activator, IIocResolver iocResolver)
            {
                _activator = activator;
                _iocResolver = iocResolver;
                _resolvedObjects = new List<object>();
            }

            public override object Resolve(Type type)
            {
                var instance = _activator.ActivateJob(type);
                _resolvedObjects.Add(instance);
                return instance;
            }

            public override void DisposeScope()
            {
                _resolvedObjects.ForEach(_iocResolver.Release);
            }
        }
    }

@IanYates
Copy link
Author

The job activator scope is the new thing introduced in Hangfire 1.5. The old approach still works but the newer approach is probably better. I haven't updated to 1.5 since I didn't want to reverify the correct Windsor behaviour again.

In the code you've posted I don't see any Windsor usage - is it hidden behind the IIocResolver?

The approach seems pretty sound though. Certainly it's less hacky :)

Thanks!

@hikalkan
Copy link

IIocResolver is ASP.NET Boilerplate's IOC abstraction. You can use same by sending Windsor Container directly. As I test, it's properly working.

@whudgins
Copy link

Ended up going with @hikalkan solution for Hangfire 1.5 but wanted to say thanks @IanYates for posting this. Was stuck for a few days. Below is implementation using IWindsorContainer rather than IIocResolver for anyone else who stumbles across this.

    public class TTBJobActivator : JobActivator
    {
        private readonly IWindsorContainer _container;

        public TTBJobActivator(IWindsorContainer container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }

            _container = container;
        }

        public override object ActivateJob(Type jobType)
        {
            return _container.Resolve(jobType);
        }

        public override JobActivatorScope BeginScope()
        {
            return new HangfireIocJobActivatorScope(this, _container);
        }

        class HangfireIocJobActivatorScope : JobActivatorScope
        {
            private readonly JobActivator _activator;
            private readonly IWindsorContainer _container;

            private readonly List<object> _resolvedObjects;

            public HangfireIocJobActivatorScope(JobActivator activator, IWindsorContainer container)
            {
                _activator = activator;
                _container = container;
                _resolvedObjects = new List<object>();
            }

            public override object Resolve(Type type)
            {
                var instance = _activator.ActivateJob(type);
                _resolvedObjects.Add(instance);
                return instance;
            }

            public override void DisposeScope()
            {
                _resolvedObjects.ForEach(_container.Release);
            }
        }
    }

@skyrawrcode
Copy link

Why did you use use child container implementation for Windsor instead of a specialized Scope accessor. In the past when working with child containers they only seemed to work if the dependencies were installed inside the child container. If the dependencies were installed in the parent container even though the child container was the resolver they were still resolved and tracked by the parent container. This makes disposing the child container do little to nothing. I know child containers are a common use in with other containers but I've always had trouble using them with windsor. I was wondering how you get around these issues?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment