Skip to content

Instantly share code, notes, and snippets.

@leniency
Created June 7, 2018 22:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save leniency/eedf27b9b7ec3c1ec76c81a0efe439fb to your computer and use it in GitHub Desktop.
Save leniency/eedf27b9b7ec3c1ec76c81a0efe439fb to your computer and use it in GitHub Desktop.
Some extensions for Hangfire and Autofac. Allows multiple background tasks to be queued for Hangfire processing. Simply call the interface method - autofac resolves the concretes and then the extension will queue the method calls.
/// <summary>
/// Implementation of the IServiceLocator.
/// Mostly from https://github.com/autofac/Autofac.Extras.CommonServiceLocator,
/// minus the dependencies.
/// </summary>
public class AutofacServiceLocator : IServiceLocator
{
/// <summary>
/// The <see cref="Autofac.IComponentContext"/> from which services
/// should be located.
/// </summary>
private readonly IComponentContext _container;
/// <summary>
/// Initializes a new instance of the <see cref="Autofac.Extras.CommonServiceLocator.AutofacServiceLocator" /> class.
/// </summary>
/// <param name="container">
/// The <see cref="Autofac.IComponentContext"/> from which services
/// should be located.
/// </param>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="container" /> is null.
/// </exception>
public AutofacServiceLocator(IComponentContext container)
{
_container = container ?? throw new ArgumentNullException("container");
}
/// <summary>
/// Get an instance of the given <typeparamref name="TService"/>.
/// </summary>
/// <typeparam name="TService">Type of object requested.</typeparam>
/// <exception cref="ActivationException">if there is are errors resolving
/// the service instance.</exception>
/// <returns>The requested service instance.</returns>
public override TService GetInstance<TService>()
{
return (TService)GetService(typeof(TService));
}
/// <summary>
/// Resolves the requested service instance.
/// </summary>
/// <param name="serviceType">Type of instance requested.</param>
/// <returns>The requested service instance.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="serviceType" /> is null.
/// </exception>
public object GetService(Type serviceType)
{
return GetService(serviceType, null);
}
/// <summary>
/// Resolves the requested service instance.
/// </summary>
/// <param name="serviceType">Type of instance requested.</param>
/// <param name="key">Name of registered service you want. May be <see langword="null" />.</param>
/// <returns>The requested service instance.</returns>
/// <exception cref="System.ArgumentNullException">
/// Thrown if <paramref name="serviceType" /> is null.
/// </exception>
/// <exception cref="ServiceActivationException">if there is an error resolving
/// the service instance.</exception>
public object GetService(Type serviceType, string key)
{
if (serviceType == null)
{
throw new ArgumentNullException(nameof(serviceType));
}
try
{
return key != null
? _container.ResolveNamed(key, serviceType)
: _container.Resolve(serviceType);
}
catch (Exception ex)
{
throw new ServiceActivationException($"Activation error occurred while trying to get instance of type {serviceType.Name}", ex);
}
}
public override IEnumerable<Type> GetImplementingTypes<T>()
{
// https://stackoverflow.com/questions/14061472/get-all-registered-implementations-of-an-interface-in-autofac
return _container.ComponentRegistry.RegistrationsFor(new TypedService(typeof(T)))
.Select(r => r.Activator.LimitType)
.Distinct();
}
}
/// <summary>
/// Defines a basic service locator.
/// </summary>
public interface IServiceLocator
{
/// <summary>
/// Get an instance of the given <typeparamref name="TService"/>.
/// </summary>
TService GetInstance<TService>();
/// <summary>
/// Fetch all service types implementing the given interface type.
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <returns></returns>
IEnumerable<Type> GetImplementingTypes<TService>();
}
public static class IServiceLocatorExtensions
{
/// <summary>
/// Find all types implementing the given interface and enqueue a new
/// background task for each type and method.
/// </summary>
/// <typeparam name="TBackgroundService"></typeparam>
/// <param name="services"></param>
/// <param name="methodCall"></param>
/// <param name="typeFilters">A collection of type filters. For each registered service,
/// it will check if the filters contains the service type. If the service type is not
/// found in the filters, the service will be called. Otherwise, the filter function
/// will be evaluated.</param>
/// <param name="methodFilter">A filter function to apply to all found methods.</param>
public static void EnqueueAll<TBackgroundService>(this IServiceLocator services, Expression<Action<TBackgroundService>> methodCall)
where TBackgroundService : IBackgroundService
{
var backgroundClient = services.GetInstance<IBackgroundJobClient>();
if (methodCall == null) throw new ArgumentNullException(nameof(methodCall));
var callExpression = methodCall.Body as MethodCallExpression;
if (callExpression == null)
{
throw new ArgumentException("Expression body should be of type `MethodCallExpression`", nameof(methodCall));
}
// If we were passed a concrete, just enqueue that alone.
if (!typeof(TBackgroundService).IsInterface && !typeof(TBackgroundService).IsAbstract)
{
backgroundClient.Enqueue(methodCall);
return;
}
// Build a background job based on the common interface.
var baseJob = Job.FromExpression(methodCall);
var parameters = baseJob.Method.GetParameters().Select(p => p.ParameterType).ToArray();
var args = baseJob.Args.ToArray();
// For each type implementing the common interface, create a new
// job for each against the concrete class.
foreach (var type in services.GetImplementingTypes<TBackgroundService>())
{
var method = type.GetMethod(baseJob.Method.Name, parameters); // Fetch the implementing method on the concrete.
var job = new Job(type, method, args); // Create a new background job.
// Enqueue the job.
backgroundClient.Create(job, new EnqueuedState());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment