In one of our recent gigs, we had to use MS Azure Functions. I’ll discuss about the architecture and how we’re using Azure Functions in our SaaS cloud solution in another post. In this post though, I’m going to talk about the problem we had in Azure Functions and propose a solution for that.
Assembly Binding Redirect issue:
As you may know, in .NET, there is a feature called “Assembly Binding Redirect” that works for any type of application. This binding redirect thing, is used when your app depends on multiple assemblies (dlls) that are themselves using different versions of the same assembly. Case in point, let’s say you’re using Microsoft.Rest NuGet package that depends on Newtonsoft.Json v9.0.0.0 and also using Box.V2 NuGet package that depends on Newtonsoft.Json v10.0.0.0 . Therefore, at the run time, we need something to redirect all Newtonsoft.json dependencies to redirect to a single version (say v10.0.0.0). The reason being we can’t have multiple versions of the same assembly at the run time. For more info about the binding redirect have a look into the following link :
https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/redirect-assembly-versions
You can simply do assembly binding redirect in all types of .NET apps. You just need to add a section to your app.config or web.config file (in case of web apps) just like the following:
<?xml version="1.0" encoding="utf-8"?> <configuration> <runtime> <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> <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>
As you see, the setting redirects all references with whatever versions (from 0.0.0.0 up to 10.0.0.0) of the Newtonsoft.Json assembly to the version 10.0.0.0 , simple as that!
In the Azure Functions though, we can’t do that; at least at the time of writing this post. So if you add an app.config to your Azure Function Project, it wouldn’t take effect, as if there is no app.config. The reason being, the way that Functions run on Azure is a bit different from normal .NET apps, as they themselves run over webjobs platform. Check out the following ticket in GitHub to see how come binding redirect doesn’t work:
https://github.com/Azure/azure-webjobs-sdk-script/issues/992
It you’re working on a real project, this problem would be quite annoying, as the code doesn’t actually work once your project’s dependencies are pointing to multiple versions of the same assembly. You simply get stuck, just as we did!
How to resolve it:
However, at the time of writing this post, the problem is not resolved by Microsoft, there is a workaround for that. Let’s say we want to add something to our application settings, which is the “local.settings.json” in Azure Functions and could be accessed through the Azure Portal, just as shown below:

So we just added a new field named “BindingRedirects” to the settings and set its value like this:
[ { “ShortName”: “Newtonsoft.Json”, “RedirectToVersion”: “10.0.0.0”, “PublicKeyToken”: “30ad4fe6b2a6aeed” } ]
Which means redirect all the references to the “Newtonsoft.Json” to version 10.0.0.0
And here’s what our local.settings.json looks like:
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "BindingRedirects": "[ { \"ShortName\": \"Newtonsoft.Json\", \"RedirectToVersion\": \"10.0.0.0\", \"PublicKeyToken\": \"30ad4fe6b2a6aeed\" } ]" } }
using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.Serialization.Json; using System.Text; namespace Connectors.AzureFunctions.Application { public static class AssemblyBindingRedirectHelper { ///<summary> /// Reads the "BindingRedirecs" field from the app settings and applies the redirection on the /// specified assemblies /// </summary> public static void ConfigureBindingRedirects() { var redirects = GetBindingRedirects(); redirects.ForEach(RedirectAssembly); } private static List<BindingRedirect> GetBindingRedirects() { var result = new List<BindingRedirect>(); var bindingRedirectListJson = Environment.GetEnvironmentVariable("BindingRedirects"); using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(bindingRedirectListJson))) { var serializer = new DataContractJsonSerializer(typeof(List<BindingRedirect>)); result = (List<BindingRedirect>)serializer.ReadObject(memoryStream); } return result; } private static void RedirectAssembly(BindingRedirect bindingRedirect) { ResolveEventHandler handler = null; handler = (sender, args) => { var requestedAssembly = new AssemblyName(args.Name); if (requestedAssembly.Name != bindingRedirect.ShortName) { return null; } var targetPublicKeyToken = new AssemblyName("x, PublicKeyToken=" + bindingRedirect.PublicKeyToken).GetPublicKeyToken(); requestedAssembly.SetPublicKeyToken(targetPublicKeyToken); requestedAssembly.Version = new Version(bindingRedirect.RedirectToVersion); requestedAssembly.CultureInfo = CultureInfo.InvariantCulture; AppDomain.CurrentDomain.AssemblyResolve -= handler; return Assembly.Load(requestedAssembly); }; AppDomain.CurrentDomain.AssemblyResolve += handler; } public class BindingRedirect { public string ShortName { get; set; } public string PublicKeyToken { get; set; } public string RedirectToVersion { get; set; } } } }
The code simply reads a setting field named “bindingRedirects” from the Function app settings, deserialize it to List of BindingRedirects and goes through the list and hooks a new method to the “AppDomain.CurrentDomain.AssemblyResolve” event. That event gets triggered once a new assembly gets resolved. Whenever you write a code that uses an external assembly, as soon it run the code that calls a type from the assembly, it triggers that event and gets resolved and loaded. If the assembly is in our binding redirect list, then we load the version specified in the settings, otherwise we do nothing.
We just need to call this method only once, and before running any other code in the function app. So what I did was creating this class as a helper, it just guaranties that the code above runs only once.
public static class ApplicationHelper { private static bool IsStarted = false; private static object _syncLock = new object(); ///<summary> /// Sets up the app before running any other code /// </summary> public static void Startup() { if (!IsStarted) { lock (_syncLock) { if (!IsStarted) { AssemblyBindingRedirectHelper.ConfigureBindingRedirects(); IsStarted = true; } } } } }
Then in your Azure Function, simple call this helper. But we need to make sure that it runs before any other code, so I put it in the static constructor of the class. I’m assuming that you’re using the project template of Visual Studio 2017 Azure Function tools for development.
using System.Linq; using System.Net; using System.Net.Http; using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.Http; using Microsoft.Azure.WebJobs.Host; using System.Threading.Tasks; using Newtonsoft.Json; using System.Collections.Generic; namespace Connectors.AzureFunctions { public static class EventProcessor { static EventProcessor() { ApplicationHelper.Startup(); } [FunctionName("EventProcessor")] public static async Task<HttpResponseMessage> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log) { // Method's body, in our case, let's say it's a code which is using Newtonsoft.Json } } }
Now we should be good, and if you need to add more binding redirects, then no need to touch the code, just add the new redirect to the settings, sit back and enjoy.
Hope this workaround resolves your binding redirect issues in the Function apps and saves some time and effort!
Thank you for posting this! It resolved a problem we had in my team with using Entity Framework Core 2.0 in an Azure Function App.
LikeLiked by 1 person
My pleasure! Good to hear that you found it useful 🙂
LikeLiked by 2 people
great job. it helped me to move forward from being blocked.
LikeLiked by 1 person
Excellent! You saved me a ton of “google’ing” time today. Way to take one for the team and for the very well-written blog post.
One [defensive programming] update I would make is to bail if the BindingRedirects setting is missing or empty. Here’s my version of it:
private static List GetBindingRedirects()
{
string bindingRedirectListJson = Environment.GetEnvironmentVariable(“BindingRedirects”);
if (string.IsNullOrEmpty(bindingRedirectListJson))
{
return new List();
}
using (var memoryStream = new MemoryStream(Encoding.Unicode.GetBytes(bindingRedirectListJson)))
{
var serializer = new DataContractJsonSerializer(typeof(List));
return (List)serializer.ReadObject(memoryStream);
}
}
LikeLike
thanks Wil!
LikeLike
This issue got me stuck for more than 2 days!
Thank you very much 😀
LikeLiked by 1 person
great post. Tx!
LikeLiked by 1 person
Fantastic. Helped me with EF Core as well after much hair pulling
LikeLiked by 1 person
I’ve spent three days banging my head on a wall. You saved me. Thank you!!!
LikeLiked by 1 person
haha, we all do from time to time, take it easy :))) cheers mate!
LikeLike
Thanks for posting! This is an elegant solution compared to others found on the web around September 17. Helped me with using MongoDB Driver
LikeLike
Thank you for the post! Great Article. This helped me work with 4.7 Azure Function with EF Core 2.0
LikeLike
Thank you for this solution!!!
I ended up here after following a pluralsight tutorial on EntityFrameworkCore and having trouble running add-migration. The solution was to add an App.config file to my data library with the following contents:
LikeLike
Thanks very much for the solution!
LikeLike
my pleasure mate!
LikeLike
thank you very much!
LikeLike
your’e welcome mate
LikeLike
Did you test this in Azure Functions V2?
LikeLike
not yet. theoretically it should work, as it’s dotnet feature.
what about it? you tried and didn’t work?
LikeLike
Thanks for the post but it doesn’t work for me. The function throws exception before even executing the helper.
Executed ‘NotificationFunction’ (Failed, Id=8363848d-1981-4677-a39c-801edb79a302)
System.Private.CoreLib: Exception while executing function: NotificationFunction. RTIaaS.NotificationFunction: Could not load file or assembly ‘Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed’. Could not find or load a specific file. (Exception from HRESULT: 0x80131621). System.Private.CoreLib: Could not load file or assembly ‘Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed’.
I have included
AspnetCore SignalR Client 1.0.2
Sdk functions is 1.0.14
and Standard library is 2.0.3
not sure why it is asking for 11.0.0.0
I have added the following in the assembly redirect local settings:
“BindingRedirects”: “[ { \”ShortName\”: \”Newtonsoft.Json\”, \”RedirectToVersion\”: \”11.0.2\”, \”PublicKeyToken\”: \”30ad4fe6b2a6aeed\” } ]”,
LikeLiked by 1 person
what’s your function’s input? keep in mind that if the function’s input is something like a queue trigger and it’s typed, then Azure Function framework uses the Newtonsoft to deserialize data from the storage and the exception you mentioned might occur. to avoid that issue you can change the data type of the input to string and deserialize it within the function.
Not sure if it works in your scenario, but I’m happy to look into the issue if you get me a bit more context.
LikeLiked by 2 people
Got similar issue in v1 functions. Binding to model happens before static constructor and due to this got exception with Newtonsoft version. I solved this by moving ApplicationHelper.Startup(); to AccessTokenExtensionProvider.Initialize() method, so it’s executed before binding configuration.
LikeLike
Thanks for the response, I have got it solved now using the post here:
https://stackoverflow.com/questions/51304256/azure-functions-newtonsoft-json-load-error
The problem is about the function core tools(CLI) my VS was using. Upgarade to tools and extensions solved it.
Thanks anyways
LikeLike
awesome! thanks for your update.
Cheers!
LikeLike
This is the best! Solved a problem we were having with the System.Runtime.CompilerServices.Unsafe library. Couldn’t find a better solution anywhere else!
LikeLike
Thank you soooo much! Two days of googling and only this solution worked for me!
LikeLike
You literally saved my day! Thanks so much!
LikeLike