Injection injection using the Azure WebJobs SDK?

The problem is that the Azure WebJobs SDK only supports public static methods as job entry points, which means that there is no way to implement constructor / property insertion.

I cannot find anything about this topic in the official documentation / resources of the WebJobs SDK. The only solution I came across is based on the service locator (antivirus) template described in this post here .

Is there a good way to use the β€œcorrect” dependency injection for Azure WebJobs SDK projects?

+50
c # dependency-injection azure azure-webjobs
May 19 '15 at 14:38
source share
4 answers

The Azure WebJobs SDK now supports instance methods. Combining this with custom IJobActivator allows you to use DI.

First create a custom IJobActivator that can resolve the type of job using your favorite DI container:

public class MyActivator : IJobActivator { private readonly IUnityContainer _container; public MyActivator(IUnityContainer container) { _container = container; } public T CreateInstance<T>() { return _container.Resolve<T>(); } } 

You need to register this class using a custom JobHostConfiguration:

 var config = new JobHostConfiguration { JobActivator = new MyActivator(myContainer) }; var host = new JobHost(config); 

Then you can use a simple class with instance methods for your tasks (here I use the Unity constructor embed function):

 public class MyFunctions { private readonly ISomeDependency _dependency; public MyFunctions(ISomeDependency dependency) { _dependency = dependency; } public Task DoStuffAsync([QueueTrigger("queue")] string message) { Console.WriteLine("Injected dependency: {0}", _dependency); return Task.FromResult(true); } } 
+74
May 26 '15 at 9:23
source share

This is how I dealt with using the new SDK. Use of the IJobactivator as described by Alexander Molenkamp.

 public class ScopedMessagingProvider : MessagingProvider { private readonly ServiceBusConfiguration _config; private readonly Container _container; public ScopedMessagingProvider(ServiceBusConfiguration config, Container container) : base(config) { _config = config; _container = container; } public override MessageProcessor CreateMessageProcessor(string entityPath) { return new CustomMessageProcessor(_config.MessageOptions, _container); } private class CustomMessageProcessor : MessageProcessor { private readonly Container _container; public CustomMessageProcessor(OnMessageOptions messageOptions, Container container) : base(messageOptions) { _container = container; } public override Task<bool> BeginProcessingMessageAsync(BrokeredMessage message, CancellationToken cancellationToken) { _container.BeginExecutionContextScope(); return base.BeginProcessingMessageAsync(message, cancellationToken); } public override Task CompleteProcessingMessageAsync(BrokeredMessage message, FunctionResult result, CancellationToken cancellationToken) { var scope = _container.GetCurrentExecutionContextScope(); if (scope != null) { scope.Dispose(); } return base.CompleteProcessingMessageAsync(message, result, cancellationToken); } } } 

You can use your own MessagingProvider in your JobHostConfiguration, for example

 var serviceBusConfig = new ServiceBusConfiguration { ConnectionString = config.ServiceBusConnectionString }; serviceBusConfig.MessagingProvider = new ScopedMessagingProvider(serviceBusConfig, container); jobHostConfig.UseServiceBus(serviceBusConfig); 
+11
Nov 17 '15 at
source share

After asking my own question about how to handle the scope ... I just came up with this solution: I do not think this is perfect, but at the moment I cannot find another solution.

In my example, I am dealing with ServiceBusTrigger.

Since I use SimpleInjector , the implementation of the IJobActivator interface looks like this:

 public class SimpleInjectorJobActivator : IJobActivator { private readonly Container _container; public SimpleInjectorJobActivator(Container container) { _container = container; } public T CreateInstance<T>() { return (T)_container.GetInstance(typeof(T)); } } 

Here I am dealing with Triggered webjobs.

So, I have two dependencies:

  • Syntax:

     public interface ISingletonDependency { } public class SingletonDependency : ISingletonDependency { } 
  • And one more that should support only the response time of my function:

     public class ScopedDependency : IScopedDependency, IDisposable { public void Dispose() { //Dispose what need to be disposed... } } 

So, to have a process that runs independently of the webjob. I encapsulated my process in a class:

 public interface IBrokeredMessageProcessor { Task ProcessAsync(BrokeredMessage incommingMessage, CancellationToken token); } public class BrokeredMessageProcessor : IBrokeredMessageProcessor { private readonly ISingletonDependency _singletonDependency; private readonly IScopedDependency _scopedDependency; public BrokeredMessageProcessor(ISingletonDependency singletonDependency, IScopedDependency scopedDependency) { _singletonDependency = singletonDependency; _scopedDependency = scopedDependency; } public async Task ProcessAsync(BrokeredMessage incommingMessage, CancellationToken token) { ... } } 

So, now that the webjob starts, I need to register dependencies based on their areas:

 class Program { private static void Main() { var container = new Container(); container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle(); container.RegisterSingleton<ISingletonDependency, SingletonDependency>(); container.Register<IScopedDependency, ScopedDependency>(Lifestyle.Scoped); container.Register<IBrokeredMessageProcessor, BrokeredMessageProcessor>(Lifestyle.Scoped); container.Verify(); var config = new JobHostConfiguration { JobActivator = new SimpleInjectorJobActivator(container) }; var servicebusConfig = new ServiceBusConfiguration { ConnectionString = CloudConfigurationManager.GetSetting("MyServiceBusConnectionString") }; config.UseServiceBus(servicebusConfig); var host = new JobHost(config); host.RunAndBlock(); } } 

And this is a running job:

  • Only one dependency: IoC container. Since this class is part of my composition root, it should be fine.
  • It handles the scope in a running function.

     public class TriggeredJob { private readonly Container _container; public TriggeredJob(Container container) { _container = container; } public async Task TriggeredFunction([ServiceBusTrigger("queueName")] BrokeredMessage message, CancellationToken token) { using (var scope = _container.BeginExecutionContextScope()) { var processor = _container.GetInstance<IBrokeredMessageProcessor>(); await processor.ProcessAsync(message, token); } } } 
+7
Dec 08 '15 at 1:28
source share

I used a couple of templates that rely on the concept of child containers / areas (depending on the terminology of your IoC container of your choice). Not sure which of them support it, but I can say that StructureMap 2.6.x and AutoFac.

The idea is to expand the content area for each incoming message, embed any context unique to this request, allow the top-level object from the child area, and then start your process.

Here is some generalized code showing it with AutoFac. This makes a direct solution from the container, similar to the anti-pattern you are trying to avoid, but it was isolated from one place.

In this case, it uses ServiceBusTrigger to start the task, but it can be anything - the task host can potentially have a list of them for different queues / processes.

 public static void ServiceBusRequestHandler([ServiceBusTrigger("queuename")] ServiceBusRequest request) { ProcessMessage(request); } 

This method is called by all instances of the above methods. He completes the creation of a child region in the use block to ensure that things are cleaned. Then, any objects that will change depending on the request and contain the context used by other dependencies (user / client information, etc.) will be created and entered into the child container (in this example, IRequestContext). Finally, the component performing the work will be resolved from the child container.

 private static void ProcessMessage<T>(T request) where T : IServiceBusRequest { try { using (var childScope = _container.BeginLifetimeScope()) { // create and inject things that hold the "context" of the message - user ids, etc var builder = new ContainerBuilder(); builder.Register(c => new ServiceRequestContext(request.UserId)).As<IRequestContext>().InstancePerLifetimeScope(); builder.Update(childScope.ComponentRegistry); // resolve the component doing the work from the child container explicitly, so all of its dependencies follow var thing = childScope.Resolve<ThingThatDoesStuff>(); thing.Do(request); } } catch (Exception ex) { } } 
+4
May 19 '15 at 21:07
source share



All Articles