1
votes

I am building an Azure WebJob that will run continuously. I want the WebJob to use the same style of DI as ASP.NET Core. I found this answer (Dependency injection using Azure WebJobs SDK?) on how to get DI/IoC working by using a custom IJobActivator. I also found this answer (Azure WebJobs - No functions found - How do I make a trigger-less job?) on using NoAutomaticTrigger to configure a continuously running Web Job.

The problem is that the NoAutomaticTrigger approach uses static methods, which is not compatible with DI/IoC. I assume that is because there is nothing that would cause the service with the NoAutomaticTrigger method to get resolved (like a queue message). I feel like ITypeLocator might be a path forward, but that is just a hunch.

The JobHost.Call method used in other NoAutomaticTrigger examples is limited to a static method. How do I have a service that is resolved via DI that gets resolved and called from a call on JobHost (or some other method) in Program.cs?


The solution that Iza Eddi-son Atsou suggested below works, with one wrinkle. I am used to registering my services via an interface on the service. Something like

serviceCollection.AddSingleton<IApplication, Application>();

The problem with this is that if the NoAutomaticTrigger attribute is on the interface, the SDK blows up. The solution is to add the attribute to the class, and register the class as itself.

serviceCollection.AddSingleton<Application>();

Once that change is made, the solution in the answer works great.

2

2 Answers

4
votes

The WebJobs SDK now supports instance methods to be triggered instead of only static methods. The NoAutomaticTrigger method don't need to be static anymore. Update the SDK, use the custom IJobActivator approach you found and remember to register you Functions class and of course your other dependencies.

Example Program class:

class Program
{

    static void Main()
    {
        var container = new UnityContainer();

        //the instance to be injected
        var systemClient = new JobSystemClient
        {
            UserName = "admin",
            PassWord = "admin1234"
        };

        container.RegisterInstance<ISystemClient>(systemClient);

        //Registration of the Functions class
        container.RegisterType<Functions>();            

        var activator = new UnityJobActivator(container);

        var config = new JobHostConfiguration();
        config.JobActivator = activator;            

        var host = new JobHost(config);
        // The following code will invoke a function called ManualTrigger and 
        // pass in data (value in this case) to the function
        host.Call(typeof(Functions).GetMethod("ManualTrigger"), new { value = 20 });

        host.RunAndBlock();

    }
}

sample Function class:

public class Functions
{
    private readonly ISystemClient _systemClient;
    public Functions(ISystemClient systemClient)
    {
        _systemClient = systemClient;
    }

    //Not static anymore
    [NoAutomaticTrigger]
    public void ManualTrigger(TextWriter log, int value, [Queue("queue")] out string message)
    {
        log.WriteLine("Function is invoked with value={0}", value);
        message = value.ToString();         
        log.WriteLine("username:{0} and password:{1}", _systemClient.UserName, _systemClient.PassWord);
    }
}

and here is the output:

Found the following functions:
TestWebJob.Functions.ManualTrigger
Executing 'Functions.ManualTrigger' (Reason='This function was programmatically called via the host APIs.', Id=bf9aedc0-89d1-4ba0-a33e-9b23e0d7b8a2)
Function is invoked with value=20
Following message will be written on the Queue=20
username:admin and password:admin1234
Executed 'Functions.ManualTrigger' (Succeeded, Id=bf9aedc0-89d1-4ba0-a33e-9b23e0d7b8a2)
Job host started
1
votes

AFAIK, The NoAutomaticTrigger attribute is useful in two cases: Functions with triggers Prevents automatic invocation of the triggers, allowing manual polling. Functions without other attributes Flags the function as an available job function. In both cases, functions marked with this attribute are never called automatically by JobHost (during RunAndBlock). Instead, they must be invoked manually using the Call method. And, this attribute can also used in the non-static method.

here are some related articles about using Azure WebJobs and Dependency Injection, you could refer to them: http://www.ryansouthgate.com/2016/05/10/azure-webjobs-and-dependency-injection/ and http://www.jerriepelser.com/blog/using-autofac-and-common-service-locator-with-azure-webjobs