Categories
App Service Azure Microsoft WebJobs

Receive emails from Azure WebJobs when errors are detected

We will learn how to be notified via email when errors occur in an Azure WebJob.

When an Azure WebJob is executed wouldn’t it be good to be notified when something goes wrong?

Thanks to the following extensions this is something we can easily do:

WebJobs ErrorTrigger extension

WebJobs SendGrid extension

 

In this article, we will discover how to send emails from an Azure WebJob with the SenGrid extension when errors are detected using the ErrorTrigger extension. As a base, we will create a project with the Azure WebJob template in Visual Studio.

 

Creation

The first thing you will need is to add the following NuGet package to your project: Microsoft.Azure.WebJobs.Extensions.SendGrid

 

To enable the ErrorTrigger and SendGrid extensions in your WebJob you will need to configure the JobHostConfiguration like the following:

using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.ErrorsEmails
{
    // To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
    class Program
    {
        // Please set the following connection strings in app.config for this WebJob to run:
        // AzureWebJobsDashboard and AzureWebJobsStorage
        static void Main()
        {
            var config = new JobHostConfiguration();

            if (config.IsDevelopment)
            {
                config.UseDevelopmentSettings();
            }

            config.UseCore();
            config.UseSendGrid();

            var host = new JobHost(config);
            // The following code ensures that the WebJob will be running continuously
            host.RunAndBlock();
        }
    }
}

The important part here is to call the extensions named UseCore and UseSendGrid. This will enable the ErrorTrigger and SendGrid extensions.

 

We will now add a new application settings to be able to connect to SendGrid:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <!-- The format of the connection string is "DefaultEndpointsProtocol=https;AccountName=NAME;AccountKey=KEY" -->
    <!-- For local execution, the value can be set either in this config file or through environment variables -->
    <add name="AzureWebJobsDashboard" connectionString="" />
    <add name="AzureWebJobsStorage" connectionString="" />
  </connectionStrings>
  <appSettings>
    <add key="AzureWebJobsSendGridApiKey" value="" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.1.4.0" newVersion="8.1.4.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
  • AzureWebJobsSendGridApiKey: an API Key related to your SendGrid account.

Now using the default ProcessQueueMessage function that uses a QueueTrigger, we will call the ProcessMessage method. This method checks if the message is empty. If it is, an exception will be thrown.

Then we create a function named GlobalErrorMonitor. This function will catch errors and send emails using SendGrid:

using System;
using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions;
using SendGrid.Helpers.Mail;

namespace AzureWebJobs.ErrorsEmails
{
    public class Functions
    {
        // This function will get triggered/executed when a new message is written 
        // on an Azure Queue called queue.
        public static void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log)
        {
            log.WriteLine(message);

            ProcessMessage(message);
        }

        /// <summary>
        /// Triggered when an error is reported in other functions.
        /// Called whenever 2 errors occur within a 3 minutes sliding window (throttled at a maximum of 2 notifications per 10 minutes).
        /// </summary>
        public static void GlobalErrorMonitor([ErrorTrigger("0:03:00", 2, Throttle = "0:10:00")] TraceFilter filter, TextWriter log, [SendGrid(From = "no-reply@anydomainxyz.com", To = "anybody@anydomainxyz.com")] out Mail mail)
        {
            mail = new Mail();

            mail.Subject = "WebJob - Warning - An error has been detected in a job";
            mail.AddContent(new Content("text/plain", filter.GetDetailedMessage(1)));

            Console.Error.WriteLine("An error has been detected in a function.");

            log.WriteLine(filter.GetDetailedMessage(1));
        }

        private static void ProcessMessage(string message)
        {
            if (string.IsNullOrEmpty(message))
            {
                throw new ArgumentNullException(nameof(message));
            }

            //Do some work here...
        }
    }
}

You can notice two things here:

  • We use the ErrorTrigger attribute to be able to use ErrorTrigger extension in the GlobalErrorMonitor function.
  • We use the SendGrid attribute to be able to use SendGrid in the GlobalErrorMonitor function.

 

Example of use

Once the WebJob ready and properly configured, we will launch it:

Found the following functions:
AzureWebJobs.ErrorsEmails.Functions.ProcessQueueMessage
Job host started

Now add an empty message to the queue. The ProcessQueueMessage function will be called 5 times and the message will be moved to the queue-poison.

After the second exception, you can notice that GlobalErrorMonitor is triggered:

Executing 'Functions.ProcessQueueMessage' (Reason='New queue message detected on 'queue'.', Id=8ee22555-15df-458e-8a0e-cdd1b9b2a97a)
Executing 'Functions.GlobalErrorMonitor' (Reason='Error trigger fired', Id=8b368696-7dab-4908-af53-18018abd8f3e)
An error has been detected in a function.
2 events at level 'Error' or lower have occurred within time window 00:03:00.

05/16/2017 02:41:20 Error Exception while executing function: Functions.ProcessQueueMessage WebJobs.Execution
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: Functions.ProcessQueueMessage ---> System.ArgumentNullException:
Value cannot be null. Parameter name: message

Executed 'Functions.GlobalErrorMonitor' (Succeeded, Id=8b368696-7dab-4908-af53-18018abd8f3e)
Executed 'Functions.ProcessQueueMessage' (Succeeded, Id=8ee22555-15df-458e-8a0e-cdd1b9b2a97a)

 You should receive an email to the address you’ve specified in the To property of the SendGrid attribute. The title will be as WebJob – Warning – An error has been detected in a job, and the body will contain the exception with the stack trace.

 

To go further

The error trigger is configured to be called whenever 2 errors occur within a 3 minutes sliding window throttled at a maximum of 2 notifications per 10 minutes.

You can easily configure it differently to receive less notifications:

public static void GlobalErrorMonitor([ErrorTrigger("0:10:00", 5, Throttle = "1:00:00")] TraceFilter filter, TextWriter log, [SendGrid(From = "no-reply@anydomainxyz.com", To = "anybody@anydomainxyz.com")] out Mail mail)

 

Summary

We have seen how to send emails from an Azure WebJob with the SenGrid extension when errors are detected using the ErrorTrigger extension.

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 2.0.0)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
App Service Azure ErrorTrigger extension Microsoft WebJobs

Monitoring errors in Azure WebJobs with the ErrorTrigger extension

We will learn in a simple scenario how to monitor errors in an Azure WebJob.

When developing jobs with Azure WebJob, it’s a good practice to implement error monitoring in case something goes wrong when a job is executed.

The WebJobs ErrorTrigger extension, part of the Core extensions, will help us achieve that.

 

Today we will learn how to monitor errors with Azure WebJob using the ErrorTrigger extension. As a base, we will create a project with the Azure WebJob template in Visual Studio.

 

Creation

The first thing you will need is to add the following NuGet package to your project: Microsoft.Azure.WebJobs.Extensions

 

To enable the ErrorTrigger extension in your WebJob you will need to configure the JobHostConfiguration like the following:

using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.ErrorTriggerExtension
{
	// To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
	class Program
	{
		// Please set the following connection strings in app.config for this WebJob to run:
		// AzureWebJobsDashboard and AzureWebJobsStorage
		static void Main()
		{
			var config = new JobHostConfiguration();

			if (config.IsDevelopment)
			{
				config.UseDevelopmentSettings();
			}

			config.UseCore();

			var host = new JobHost(config);
			// The following code ensures that the WebJob will be running continuously
			host.RunAndBlock();
		}
	}
}

The important part here is to call the extension named UseCore. This will enable the ErrorTrigger extension.

 

Now we will create two functions ProcessQueueAMessage and ProcessQueueBMessage using the default ProcessQueueMessage function that uses a QueueTrigger.

Both will call the ProcessMessage method. This method checks if the message is empty. If it is, an exception will be thrown.

ProcessQueueAMessage will simply call ProcessMessage, ProcessQueueBMessage will also call it but catching exceptions and logging them using the TraceWriter.

Then we create a function named GlobalErrorMonitor, giving the following code:

using System;
using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions;
using Microsoft.Azure.WebJobs.Host;

namespace AzureWebJobs.ErrorTriggerExtension
{
	public class Functions
	{
		// This function will get triggered/executed when a new message is written 
		// on an Azure Queue called queue.
		public static void ProcessQueueAMessage([QueueTrigger("queuea")] string message, TextWriter log)
		{
			log.WriteLine(message);

			ProcessMessage(message);
		}

		public static void ProcessQueueBMessage([QueueTrigger("queueb")] string message, TraceWriter logger)
		{
			logger.Info(message);

			try
			{
				ProcessMessage(message);
			}
			catch (Exception ex)
			{
				logger.Error($"An error occurred in: '{nameof(ProcessQueueBMessage)}'", ex, nameof(Functions));
			}
		}

		/// <summary>
		/// Triggered when an error is reported in other functions.
		/// Called whenever 2 errors occur within a 3 minutes sliding window (throttled at a maximum of 2 notifications per 10 minutes).
		/// </summary>
		public static void GlobalErrorMonitor([ErrorTrigger("0:03:00", 2, Throttle = "0:10:00")] TraceFilter filter, TextWriter log)
		{
			Console.Error.WriteLine("An error has been detected in a function.");

			log.WriteLine(filter.GetDetailedMessage(1));
		}

		private static void ProcessMessage(string message)
		{
			if (string.IsNullOrEmpty(message))
			{
				throw new ArgumentNullException(nameof(message));
			}

			//Do some work here...
		}
	}
}

You can notice two things here:

  • We use the ErrorTrigger attribute to be able to use ErrorTrigger extension in the GlobalErrorMonitor function.
  • The error trigger is configured to be called whenever 2 errors occur within a 3 minutes sliding window (throttled at a maximum of 2 notifications per 10 minutes.

 

Example of use

Once the WebJob ready and properly configured, we will launch it:

Found the following functions:
AzureWebJobs.ErrorTriggerExtension.Functions.ProcessQueueAMessage
AzureWebJobs.ErrorTriggerExtension.Functions.ProcessQueueBMessage
AzureWebJobs.ErrorTriggerExtension.Functions.GlobalErrorMonitor
Job host started

Now add an empty message to the queuea. The ProcessQueueAMessage function will be called 5 times and the message will be moved to the queuea-poison.

After the second exception you can notice that GlobalErrorMonitor is triggered:

Executing 'Functions.ProcessQueueAMessage' (Reason='New queue message detected on 'queuea'.', Id=8ee22555-15df-458e-8a0e-cdd1b9b2a97a)
Executing 'Functions.GlobalErrorMonitor' (Reason='Error trigger fired', Id=8b368696-7dab-4908-af53-18018abd8f3e)
An error has been detected in a function.
2 events at level 'Error' or lower have occurred within time window 00:03:00.

04/11/2017 23:29:43 Error Exception while executing function: Functions.ProcessQueueAMessage WebJobs.Execution Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: Functions.ProcessQueueAMessage ---> System.ArgumentNullException: Value cannot be null.
Parameter name: message
 at AzureWebJobs.ErrorTriggerExtension
 ...

Executed 'Functions.GlobalErrorMonitor' (Succeeded, Id=8b368696-7dab-4908-af53-18018abd8f3e)
Executed 'Functions.ProcessQueueBMessage' (Succeeded, Id=8ee22555-15df-458e-8a0e-cdd1b9b2a97a)

 

Now restart the WebJob and add an empty message to the queueb. The ProcessQueueBMessage function will be called once as we are catching and logging the ArgumentNullException.

Notice that the GlobalErrorMonitor is not executed as it is configured to be triggered when at least 2 errors occur.

Now add another empty message to the queueb and you will now notice that GlobalErrorMonitor is triggered:

Executing 'Functions.ProcessQueueBMessage' (Reason='New queue message detected on 'queueb'.', Id=9ba82a41-fe9c-4227-a306-9af2dc9db7c2)
Executing 'Functions.GlobalErrorMonitor' (Reason='Error trigger fired', Id=03a3c8ea-16cf-46e9-a6c5-074a4f47c997)
An error has been detected in a function.
2 events at level 'Error' or lower have occurred within time window 00:03:00.

04/11/2017 23:43:05 Error An error occurred in: 'ProcessQueueBMessage' Functions System.ArgumentNullException: Value cannot be null.
Parameter name: message
 at AzureWebJobs.ErrorTriggerExtension
 ...

Executed 'Functions.GlobalErrorMonitor' (Succeeded, Id=03a3c8ea-16cf-46e9-a6c5-074a4f47c997)
Executed 'Functions.ProcessQueueBMessage' (Succeeded, Id=9ba82a41-fe9c-4227-a306-9af2dc9db7c2)

 

To go further

Here we have setup a global error handler and you could setup more handlers, but you can also have function specific error handlers.

To create a function specific handler use the naming convention based on the “ErrorHandler” suffix.

For example, if we want to have a specific handler for the function ProcessQueueAMessage we will would create the following:

public static void ProcessQueueAMessageErrorHandler([ErrorTrigger("0:03:00", 2, Throttle = "0:10:00")] TraceFilter filter, TextWriter log)

In that case only errors coming from the function ProcessQueueAMessage will trigger the ErrorTrigger extension.

 

Summary

We have learned how to monitor errors with Azure WebJob using the ErrorTrigger extension.

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 2.0.0)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
Azure Microsoft SendGrid extension WebJobs

How to send emails with SendGrid in Azure WebJobs using the SendGrid extension

Do you want to send emails in an Azure WebJob? In a simple scenario we will discover how to do it with SendGrid.

When you need to send emails from an Azure WebJob, the following extension is very helpful:

WebJobs SendGrid extension

Today we will take a look at the version 2.0 that came along with the release of the Azure WebJobs SDK 2.0.

In this article, we will discover how to send a simple email from an Azure WebJob with the SenGrid extension. As a base, we will create a project with the Azure WebJob template in Visual Studio.

 

Creation

The first thing you will need is to add the following NuGet package to your project: Microsoft.Azure.WebJobs.Extensions.SendGrid

 

To enable the SendGrid extension in your WebJob you will need to configure the JobHostConfiguration like the following:

using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.SendGridExtension
{
	// To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
	class Program
	{
		// Please set the following connection strings in app.config for this WebJob to run:
		// AzureWebJobsDashboard and AzureWebJobsStorage
		static void Main()
		{
			var config = new JobHostConfiguration();

			if (config.IsDevelopment)
			{
				config.UseDevelopmentSettings();
			}

			config.UseSendGrid();

			var host = new JobHost(config);
			// The following code ensures that the WebJob will be running continuously
			host.RunAndBlock();
		}
	}
}

The important part here is to call the extension named UseSendGrid. This will enable the SendGrid extension.

 

We will now add a new application settings to be able to connect to SendGrid:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <!-- The format of the connection string is "DefaultEndpointsProtocol=https;AccountName=NAME;AccountKey=KEY" -->
    <!-- For local execution, the value can be set either in this config file or through environment variables -->
    <add name="AzureWebJobsDashboard" connectionString="" />
    <add name="AzureWebJobsStorage" connectionString="" />
  </connectionStrings>
  <appSettings>
    <add key="AzureWebJobsSendGridApiKey" value="" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.1.1.0" newVersion="8.1.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
  • AzureWebJobsSendGridApiKey: an API Key related to your SendGrid account.

 

Now using the default ProcessQueueMessage function that uses a QueueTrigger, we want to send an email when a message is added to the queue:

using System.IO;
using Microsoft.Azure.WebJobs;
using SendGrid.Helpers.Mail;

namespace AzureWebJobs.SendGridExtension
{
	public class Functions
	{
		// This function will get triggered/executed when a new message is written 
		// on an Azure Queue called queue.
		public static void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, [SendGrid(From = "no-reply@anydomainxyz.com", To = "anybody@anydomainxyz.com")] out Mail mail)
		{
			log.WriteLine(message);

			mail = new Mail();

			mail.Subject = "WebJob - Queue message processed successfully";
			mail.AddContent(new Content("text/plain", $"The message '{message}' was successfully processed."));
		}
	}
}

You can notice several things here:

  • We use the SendGrid attribute to be able to use SendGrid in the function.
  • The out contextual keyword with the mail parameter allow us to pass the email to the extension that will then send it to SendGrid.
  • The Mail object is coming directly from the SendGrid library.
  • Update the To address, so you can receive the emails.

 

Example of use

Once the WebJob ready and properly configured, we will launch it and add a message to the queue:

Found the following functions:
AzureWebJobs.SendGridExtension.Functions.ProcessQueueMessage
Job host started
Executing 'Functions.ProcessQueueMessage' (Reason='New queue message detected on 'queue'.', Id=ef4a15bf-ce79-4be1-8b56-a3237b929b50)
My queue test message
Executed 'Functions.ProcessQueueMessage' (Succeeded, Id=ef4a15bf-ce79-4be1-8b56-a3237b929b50)

If everything goes well, you should see the same kind of message in console output as above.

You should also receive an email to the address you’ve specified in the To property of the SendGrid attribute. The title will be as WebJob – Queue message processed successfully, and the body will contain the message coming from the queue like the following: The message ‘My queue test message’ was successfully processed.

 

To go further

If you want to send more than one email, we can use the WebJob IAsyncCollector like the following:

public static async Task ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, [SendGrid(From = "no-reply@anydomainxyz.com", To = "anybody@anydomainxyz.com")] IAsyncCollector mails)

There are many other ways to send emails from the extension, more details can be found here:

SendGrid Extension Samples

 

Summary

We have seen how to send emails from an Azure WebJob using the SendGrid extension.

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 2.0.0)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
App Service Azure Microsoft Notification Hubs extension WebJobs

Sending push notifications in Azure WebJobs with Azure Notification Hubs extension

In a simple scenario we will discover how to send a push notification to FCM/GCM in an Azure WebJob.

After several months in Beta, Azure WebJobs SDK 2.0 has been released March 1st 2017:

Azure WebJobs SDK 2.0 released

Along with this release several new extensions are now available. Today we will take a look at the version 1.0 of the following:

WebJobs Notification Hubs extension

 

Thanks to this extension, sending push notifications from an Azure WebJob become an easy task.

In this article, we will discover how to send a FCM/GCM (Firebase Cloud Messaging/Google Cloud Messaging) notification from an Azure WebJob with the Azure Notification Hubs extension. As a base, we will create a project with the Azure WebJob template in Visual Studio.

 

Creation

The first thing you will need is an Azure Notification Hub deployed on Azure. If don’t know how, you can quickly deploy one with ARM template. Everything is detailed for you in a previous article.

 

Once your Notification Hub deployed and setup, add the following NuGet package to your project: Microsoft.Azure.WebJobs.Extensions.NotificationHubs

 

To enable the Notifications Hubs extension in your WebJob you will need to configure the JobHostConfiguration like the following:

using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.NotificationHubExtension
{
	// To learn more about Microsoft Azure WebJobs SDK, please see https://go.microsoft.com/fwlink/?LinkID=320976
	class Program
	{
		// Please set the following connection strings in app.config for this WebJob to run:
		// AzureWebJobsDashboard and AzureWebJobsStorage
		static void Main()
		{
			var config = new JobHostConfiguration();

			if (config.IsDevelopment)
			{
				config.UseDevelopmentSettings();
			}

			config.UseNotificationHubs();

			var host = new JobHost(config);
			// The following code ensures that the WebJob will be running continuously
			host.RunAndBlock();
		}
	}
}

The important part here is to call the extension named UseNotificationHubs. This will enable the Notification Hubs extension.

 

We will now add two new application settings to be able to connect to Azure Notification Hubs:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <!-- The format of the connection string is "DefaultEndpointsProtocol=https;AccountName=NAME;AccountKey=KEY" -->
    <!-- For local execution, the value can be set either in this config file or through environment variables -->
    <add name="AzureWebJobsDashboard" connectionString="" />
    <add name="AzureWebJobsStorage" connectionString="" />
  </connectionStrings>
  <appSettings>
    <add key="AzureWebJobsNotificationHubsConnectionString" value="Endpoint=sb://mynotifhubnamespace.servicebus.windows.net/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=U3Mxxx" />
    <add key="AzureWebJobsNotificationHubName" value="myNotifHub" />
  </appSettings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Azure.KeyVault.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.1.1.0" newVersion="8.1.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
  • AzureWebJobsNotificationHubsConnectionString: the connection string of the Azure Notification Hub Namespace.
  • AzureWebJobsNotificationHubName: the name of the Azure Notification Hub.

 

Now using the default ProcessQueueMessage function that uses a QueueTrigger, we want to send a notification when a message is added to the queue:

using System.IO;
using AzureWebJobs.NotificationHubExtension.Extensions;
using Microsoft.Azure.NotificationHubs;
using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.NotificationHubExtension
{
	public class Functions
	{
		// This function will get triggered/executed when a new message is written 
		// on an Azure Queue called queue.
		public static void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, [NotificationHub] out Notification notification)
		{
			log.WriteLine(message);

			notification = new GcmNotification(message.ToGcmPayload());
		}
	}
}

You can notice two things here:

  • We use the NotificationHub attribute to be able to use Notification Hubs in the function.
  • The out contextual keyword with the notification parameter allow us to pass the GcmNotification to the extension that will then send the notification to Notification Hub.

 

Example of use

Once the WebJob ready and properly configured, we will launch it and add a message to the queue:

Found the following functions:
AzureWebJobs.NotificationHubExtension.Functions.ProcessQueueMessage
Job host started
Executing 'Functions.ProcessQueueMessage' (Reason='New queue message detected on 'queue'.', Id=f7767465-adb0-46d7-83f4-926f1c526074)
Notification Hub test notification from WebJob
Executed 'Functions.ProcessQueueMessage' (Succeeded, Id=f7767465-adb0-46d7-83f4-926f1c526074)

If everything goes well, you should see the same kind of message in console output as above.

 

The ToGcmPayload() extension will generate the following payload and will be sent in the notification:

"{\"data\":{\"message\":\"Notification Hub test notification\"}}"

 

To go further

If you want to send more than one notification, the out parameter can be an array of notification like the following:

public static void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, [NotificationHub] out Notification[] notifications)

There are many other ways to send notifications from the extension like template notification, more details can be found here:

Notification Hubs Extension Samples

 

Summary

We have seen how to send a notification from an Azure WebJob using the Azure Notification Hubs extension.

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 2.0.0)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
App Service Azure Microsoft WebJobs

Job Handlers and Dependency Injection in Azure WebJobs

In the series of articles about Microsoft Azure WebJob and Dependency Injection, we will learn how to process a function with a Job Handler.

In a previous article we discovered in an advanced scenario how to implement Dependency Injection and Dependency Scope per job in Azure WebJobs with Unity.

Today in this last article in the series of articles about Microsoft Azure WebJob, we will discover how to process a function using a Job Handler.

 

Creation

Let’s continue with the source code created in the previous article by creating the following interface:

using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace AzureWebJobs.JobActivatorUnity.Contracts
{
    public interface IQueueMessageJob
    {
        Task Process(CancellationToken ct, string message, TextWriter log);
    }
}

 

Now we will create the implementation of it:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AzureWebJobs.JobActivatorUnity.Contracts;

namespace AzureWebJobs.JobActivatorUnity.Handlers
{
    public sealed class QueueMessageJobHandler : IQueueMessageJob
    {
        private readonly INumberService numberService;
        private readonly IUnitOfWork unitOfWork;

        public QueueMessageJobHandler(INumberService numberService, IUnitOfWork unitOfWork)
        {
            if (numberService == null) throw new ArgumentNullException(nameof(numberService));
            if (unitOfWork == null) throw new ArgumentNullException(nameof(unitOfWork));

            this.numberService = numberService;
            this.unitOfWork = unitOfWork;
        }

        public async Task Process(CancellationToken ct, string message, TextWriter log)
        {
            Console.WriteLine("Beginning QueueMessageJobHandler work...");

            log.WriteLine("New random number {0} from number service for message: {1}", this.numberService.GetRandomNumber(), message);

            await this.unitOfWork.DoWork(ct, message);

            Console.WriteLine("Finishing QueueMessageJobHandler work...");
        }
    }
}

As you can notice we moved the code previously in the function to the Job Handler process method.

  

Example of use

We have created the QueueMessageJobHandler and we will learn how to use it.

  

First we will register it in the Unity container:

using System;
using AzureWebJobs.JobActivatorUnity.Contracts;
using AzureWebJobs.JobActivatorUnity.Dependencies;
using AzureWebJobs.JobActivatorUnity.Handlers;
using AzureWebJobs.JobActivatorUnity.Services;
using AzureWebJobs.JobActivatorUnity.Unity;
using Microsoft.Practices.Unity;

namespace AzureWebJobs.JobActivatorUnity
{
    public class UnityConfig
    {
        #region Unity Container
        private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
        {
            var container = new UnityContainer();
            RegisterTypes(container);
            return container;
        });

        /// <summary>
        /// Gets the configured Unity container.
        /// </summary>
        public static IUnityContainer GetConfiguredContainer()
        {
            return container.Value;
        }
        #endregion

        /// <summary>Registers the type mappings with the Unity container.</summary>
        /// <param name="container">The unity container to configure.</param>
        public static void RegisterTypes(IUnityContainer container)
        {
            container.RegisterType<IJobActivatorDependencyResolver, UnityJobActivatorHierarchicalDependencyResolver>(new ContainerControlledLifetimeManager(), new InjectionConstructor(container.CreateChildContainer()));
            container.RegisterType<INumberService, NumberService>(new ContainerControlledLifetimeManager());
            container.RegisterType<IQueueMessageJob, QueueMessageJobHandler>(new HierarchicalLifetimeManager());
            container.RegisterType<IUnitOfWork, DisposableService>(new HierarchicalLifetimeManager());
        }
    }
}

Here we register the Job Handler along with the other services.

  

In our job function we can now use the IQueueMessageJob:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AzureWebJobs.JobActivatorUnity.Contracts;
using AzureWebJobs.JobActivatorUnity.Dependencies;
using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.JobActivatorUnity
{
    public class Functions
    {
        private readonly IJobActivatorDependencyResolver jobActivatorDependencyResolver;

        public Functions(IJobActivatorDependencyResolver jobActivatorDependencyResolver)
        {
            if (jobActivatorDependencyResolver == null) throw new ArgumentNullException(nameof(jobActivatorDependencyResolver));

            this.jobActivatorDependencyResolver = jobActivatorDependencyResolver;
        }

        // This function will get triggered/executed when a new message is written 
        // on an Azure Queue called queue.
        public async Task ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, CancellationToken ct)
        {
            using (var scope = this.jobActivatorDependencyResolver.BeginScope())
            {
                await scope.CreateInstance<IQueueMessageJob>().Process(ct, message, log);
            }
        }
    }
}

We can pay attention to several things here:

  • The function is simplified and all the work is now done in the Job Handler.
  • The job is processed in its own scope.
  • We call CreateInstance<IQueueMessageJob>() once, all the dependencies will be automatically injected by Unity in the implementation.
  • All the instances created inside the scope will be disposed immediately before the function ends.

 

To go further

If you test the source code, the console output will be the following when the function is triggered:

Executing: 'Functions.ProcessQueueMessage' - Reason: 'New queue message detected on 'queue'.'
Beginning QueueMessageJobHandler work...
DisposableService doing work...
Finishing QueueMessageJobHandler work...
DisposableService disposing...
Executed: 'Functions.ProcessQueueMessage' (Succeeded)

Right after the Job Handler work is done, the disposable service is disposed.

 

Summary

We have seen how to create a Job Handler for a Microsoft Azure WebJob function which completes the series of articles about Microsoft Azure WebJob and Dependency Injection.

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 1.1.2)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
App Service Azure Microsoft WebJobs

Implementing Dependency Injection and Dependency Scope per job in Azure WebJobs with Unity

In this article we will discover in a simple scenario how to use Unity in a Microsoft Azure WebJob.

In a previous article we discovered in a simple scenario how to use Unity in a Microsoft Azure WebJob by creating a custom IJobActivator.

Continuing this article, today we will answer the following question:

How can I get a dependency scope only for the lifetime of my function?

This question might come up for example in a scenario where you need to reach a database in a function. In this kind of scenario you want to create a DbContext, query the database, close the connection, dispose all the resources properly, same as you would do in a web application with an HTTP request. Why? Because you don’t want to end up creating a DbContext per job not knowing when everything will be disposed, you want to have the control on the dependencies lifetime.

 

Creation

Let’s continue with the source code created in the previous article by creating the following interface:

using System;

namespace AzureWebJobs.JobActivatorUnity.Dependencies
{
    public interface IJobActivatorDependencyResolver : IDisposable
    {
        IJobActivatorDependencyScope BeginScope();
    }
}

 

Now we will create the Unity implementation of it:

using System;
using AzureWebJobs.JobActivatorUnity.Dependencies;
using Microsoft.Practices.Unity;

namespace AzureWebJobs.JobActivatorUnity.Unity
{
    public class UnityJobActivatorHierarchicalDependencyResolver : IJobActivatorDependencyResolver
    {
        private readonly IUnityContainer container;

        public UnityJobActivatorHierarchicalDependencyResolver(IUnityContainer container)
        {
            if (container == null) throw new ArgumentNullException("container");

            this.container = container;
        }

        public IJobActivatorDependencyScope BeginScope()
        {
            return new UnityJobActivatorHierarchicalDependencyScope(this.container);
        }

        public void Dispose()
        {
            this.container.Dispose();
        }

        private sealed class UnityJobActivatorHierarchicalDependencyScope : IJobActivatorDependencyScope
        {
            private readonly IUnityContainer container;

            public UnityJobActivatorHierarchicalDependencyScope(IUnityContainer parentContainer)
            {
                this.container = parentContainer.CreateChildContainer();
            }

            public T CreateInstance<T>()
            {
                return this.container.Resolve<T>();
            }

            public void Dispose()
            {
                this.container.Dispose();
            }
        }
    }
}

Thanks to this implementation every time a new scope is created a specific child container is created.

 

Example of use

We have created a JobActivatorDependencyResolver and we will learn how to use it.

 

First we will register it in the Unity container along with a disposable service:

using System;
using AzureWebJobs.JobActivatorUnity.Contracts;
using AzureWebJobs.JobActivatorUnity.Dependencies;
using AzureWebJobs.JobActivatorUnity.Services;
using AzureWebJobs.JobActivatorUnity.Unity;
using Microsoft.Practices.Unity;

namespace AzureWebJobs.JobActivatorUnity
{
    public class UnityConfig
    {
        #region Unity Container
        private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
        {
            var container = new UnityContainer();
            RegisterTypes(container);
            return container;
        });

        /// <summary>
        /// Gets the configured Unity container.
        /// </summary>
        public static IUnityContainer GetConfiguredContainer()
        {
            return container.Value;
        }
        #endregion

        /// <summary>Registers the type mappings with the Unity container.</summary>
        /// <param name="container">The unity container to configure.</param>
        public static void RegisterTypes(IUnityContainer container)
        {
            container.RegisterType<IJobActivatorDependencyResolver, UnityJobActivatorHierarchicalDependencyResolver>(new ContainerControlledLifetimeManager(), new InjectionConstructor(container.CreateChildContainer()));
            container.RegisterType<INumberService, NumberService>(new ContainerControlledLifetimeManager());
            container.RegisterType<IUnitOfWork, DisposableService>(new HierarchicalLifetimeManager());
        }
    }
}

You can notice two elements here:

  • We configure the dependency resolver to be a singleton.
  • We provide the dependency resolver with its own child container. All the scopes created will be under this container.

 

In our job function we can now use the IJobActivatorDependencyResolver:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AzureWebJobs.JobActivatorUnity.Contracts;
using AzureWebJobs.JobActivatorUnity.Dependencies;
using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.JobActivatorUnity
{
    public class Functions
    {
        private readonly IJobActivatorDependencyResolver jobActivatorDependencyResolver;
        private readonly INumberService numberService;

        public Functions(IJobActivatorDependencyResolver jobActivatorDependencyResolver, INumberService numberService)
        {
            if (jobActivatorDependencyResolver == null) throw new ArgumentNullException("jobActivatorDependencyResolver");
            if (numberService == null) throw new ArgumentNullException("numberService");

            this.jobActivatorDependencyResolver = jobActivatorDependencyResolver;
            this.numberService = numberService;
        }

        // This function will get triggered/executed when a new message is written 
        // on an Azure Queue called queue.
        public async Task ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, CancellationToken ct)
        {
            log.WriteLine("New random number {0} from shared number service for message: {1}", this.numberService.GetRandomNumber(), message);

            using (var scope = this.jobActivatorDependencyResolver.BeginScope())
            {
                Console.WriteLine("Beginning scope work...");

                log.WriteLine("New random number {0} from scoped number service for message: {1}", scope.CreateInstance<INumberService>().GetRandomNumber(), message);

                await scope.CreateInstance<IUnitOfWork>().DoWork(ct, message);

                Console.WriteLine("Finishing scope work...");
            }
        }
    }
}

We can pay attention to several things here:

  • An INumberService instance is injected in our function and shared among jobs.
  • We also demonstrate the possibility to create a specific instance of INumberService for the scope.
  • The IUnitOfWork DoWork call could typically be a work against a database.
  • All the instances created inside the scope will be disposed immediately before the function ends.

 

To go further

If you test the source code, the console output will be the following when the function is triggered:

Executing: 'Functions.ProcessQueueMessage' - Reason: 'New queue message detected on 'queue'.'
Beginning scope work...
DisposableService doing work...
Finishing scope work...
DisposableService disposing...
Executed: 'Functions.ProcessQueueMessage' (Succeeded)

 

Summary

We have seen how to create a Dependency Scope in a Microsoft Azure WebJob function in a more advanced scenario which completes the previous article.

Now in the scenario we just covered there is place for improvement. Instead of calling a CreateInstance<T>() every time we need an object, we could simplify it by creating job handlers.

This will be the subject of the last article in the series about Dependency Injection in Azure WebJobs, stay tuned!

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 1.1.2)

 

Please feel free to comment or contact me if you have any question about this article.

Categories
App Service Azure Microsoft WebJobs

How to use Unity as job activator and implement Dependency Injection in Azure WebJobs

In this article we will discover in a simple scenario how to use Unity in a Microsoft Azure WebJob.

If you are a user of Azure App Service you are probably aware of the Azure WebJobs. If it’s not case the case, I advise you to take a look at it because Azure WebJobs are very powerful to run background processes and comes at no extra cost when already using Azure App Service (Web Apps, Mobile Apps, API Apps, Logic Apps).

Also Azure WebJobs come along the Azure WebJobs SDK that has a great trigger system to work with Azure Storage. For example a job function can be automatically started when a message is inserted into an Azure Queue storage.

Behind the scene the trigger system will create a new instance of the class containing the function, then the function will be invoked. The responsibility of creating instances of job classes is given to the JobActivator.

 

DefaultJobActivator

Now let’s take a look at the original source code of the DefaultJobActivator on GitHub:

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;

namespace Microsoft.Azure.WebJobs.Host.Executors
{
    internal class DefaultJobActivator : IJobActivator
    {
        private static readonly DefaultJobActivator Singleton = new DefaultJobActivator();

        private DefaultJobActivator()
        {
        }

        public static DefaultJobActivator Instance
        {
            get { return Singleton; }
        }

        public T CreateInstance<T>()
        {
            return Activator.CreateInstance<T>();
        }
    }
}

Here you can notice two things:

  • Activator.CreateInstance<T>() is used to create an instance of the specified type.
  • DefaultJobActivator implements IJobActivator.

The problem with this implementation is that only an instance of a job class with parameterless constructor can be created.

 

Fortunately for us the Azure WebJobs SDK allows to specify a custom JobActivator in the JobHostConfiguration.

In the original source code on GitHub we can see that the DefaultJobActivator is used when creating a new JobHostConfiguration:

AddService<IJobActivator>(DefaultJobActivator.Instance);

Now let’s take a look at how to replace the default job activator with our own using Unity!

 

Creation

For our needs we will create the following interface inheriting from IJobActivator:

using System;
using Microsoft.Azure.WebJobs.Host;

namespace AzureWebJobs.JobActivatorUnity.Dependencies
{
    public interface IJobActivatorDependencyScope : IJobActivator, IDisposable
    {
    }
}

 

Now we will create the Unity implementation of it:

using System;
using AzureWebJobs.JobActivatorUnity.Dependencies;
using Microsoft.Practices.Unity;

namespace AzureWebJobs.JobActivatorUnity.Unity
{
    public class UnityJobActivatorDependencyScope : IJobActivatorDependencyScope
    {
        private readonly IUnityContainer container;

        public UnityJobActivatorDependencyScope(IUnityContainer container)
        {
            if (container == null) throw new ArgumentNullException("container");

            this.container = container;
        }

        public T CreateInstance<T>()
        {
            return this.container.Resolve<T>();
        }

        public void Dispose()
        {
            this.container.Dispose();
        }
    }
}

Thanks to this implementation the responsibility of creating instances is given to the Unity container through the invocation of container.Resolve<T>().

 

Example of use

We have created a custom JobActivator, now we will learn how to use it in a simple scenario.

 

First we will configure the Unity container and register a simple service to it:

using System;
using AzureWebJobs.JobActivatorUnity.Contracts;
using AzureWebJobs.JobActivatorUnity.Services;
using Microsoft.Practices.Unity;

namespace AzureWebJobs.JobActivatorUnity
{
    public class UnityConfig
    {
        #region Unity Container
        private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
        {
            var container = new UnityContainer();
            RegisterTypes(container);
            return container;
        });

        /// <summary>
        /// Gets the configured Unity container.
        /// </summary>
        public static IUnityContainer GetConfiguredContainer()
        {
            return container.Value;
        }
        #endregion

        /// <summary>Registers the type mappings with the Unity container.</summary>
        /// <param name="container">The unity container to configure.</param>
        public static void RegisterTypes(IUnityContainer container)
        {
            container.RegisterType<INumberService, NumberService>(new ContainerControlledLifetimeManager());
        }
    }
}

Here nothing special as it’s a common Unity container configuration. The INumberService is used for the example and will allow us to return a random number.

 

Now we will configure our WebJob to use our custom IJobActivator:

using AzureWebJobs.JobActivatorUnity.Unity;
using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.JobActivatorUnity
{
    // To learn more about Microsoft Azure WebJobs SDK, please see http://go.microsoft.com/fwlink/?LinkID=320976
    class Program
    {
        // Please set the following connection strings in app.config for this WebJob to run:
        // AzureWebJobsDashboard and AzureWebJobsStorage
        static void Main()
        {
            var config = new JobHostConfiguration()
            {
                JobActivator = new UnityJobActivatorDependencyScope(UnityConfig.GetConfiguredContainer())
            };

            var host = new JobHost(config);
            // The following code ensures that the WebJob will be running continuously
            host.RunAndBlock();
        }
    }
}

 

In our job function we use can now use the INumberService:

using System;
using System.IO;
using AzureWebJobs.JobActivatorUnity.Contracts;
using Microsoft.Azure.WebJobs;

namespace AzureWebJobs.JobActivatorUnity
{
    public class Functions
    {
        private readonly INumberService numberService;

        public Functions(INumberService numberService)
        {
            if (numberService == null) throw new ArgumentNullException("numberService");

            this.numberService = numberService;
        }

        // This function will get triggered/executed when a new message is written 
        // on an Azure Queue called queue.
        public void ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log)
        {
            log.WriteLine("New random number {0} for message: {1}", this.numberService.GetRandomNumber(), message);
        }
    }
}

We can pay attention to several things here:

  • The INumberService is automatically injected in our function thanks to the Unity container.
  • When the ProcessQueueMessage function is triggered by the QueueTrigger we can seamlessly use the number service.
  • We could easily add other dependencies to the function.

 

To go further

When a function is triggered by the Azure WebJobs trigger system it is interesting to understand that a new instance of the class containing the function is created via the IJobActivator before invoking the function itself. This is important because every time a function is triggered it gives you latitude on the configuration of the dependencies’ lifetime.

 

Summary

We have seen how to use Unity as job activator and implement IoC and DI in a Microsoft Azure WebJob simple scenario.

Of course instead of using Unity you are free to use another IoC container like Autofac, Ninject, Simple Injector, etc.

 

Now in the scenario we just went through you may have asked yourself: “But how can I get a dependency scope only for the lifetime of my function?”.

Yes what if I want to get dependencies that will be just instantiated for my function and make sure that those resources will be disposed when the function’s job is done. Like reaching a database for the purpose of the function and close the connection, dispose all the resources properly. Well that will be the subject of my next article about Azure WebJobs in a more complex scenario, stay tuned!

 

You can download the example solution here:

Download full sources

Or

Browse the GitHub repository

(Note that the project uses Microsoft.Azure.WebJobs version 1.1.2)

 

Please feel free to comment or contact me if you have any question about this article.