Is it possible to include a web.config or app.config file in the azure functions folder structure to allow assembly binding redirects?
1 or a later version, the app uses automatic binding redirection. This means that if two components reference different versions of the same strong-named assembly, the runtime automatically adds a binding redirection to the newer version of the assembly in the output app configuration (app. config) file.
Binding to a function is a way of declaratively connecting another resource to the function; bindings may be connected as input bindings, output bindings, or both. Data from bindings is provided to the function as parameters. You can mix and match different bindings to suit your needs.
Unlike a trigger, a function can have multiple input and output bindings.
Assuming you are using the latest (June'17) Visual Studio 2017 Function Tooling, I derived a somewhat-reasonable config-based solution for this following a snippet of code posted by npiasecki
over on Issue #992.
It would be ideal if this were managed through the framework, but at least being configuration-driven you have a bit more change isolation. I suppose you could also use some pre-build steps or T4 templating that reconciles the versions of the nugets in the project (and their dependencies) before writing out this config or generating code.
So the downside.... becomes having to remember to update the BindingRedirects
config when you update the NuGet package (this is often a problem in app.configs anyway). You may also have an issue with the config-driven solution if you need to redirect Newtonsoft
.
In our case, we were using the new Azure Fluent NuGet that had a dependency on an older version of Microsoft.IdentityModel.Clients.ActiveDirectory
than the version of the normal ARM management libraries which are used side-by-side in a particular Function.
{
"IsEncrypted": false,
"Values": {
"BindingRedirects": "[ { \"ShortName\": \"Microsoft.IdentityModel.Clients.ActiveDirectory\", \"RedirectToVersion\": \"3.13.9.1126\", \"PublicKeyToken\": \"31bf3856ad364e35\" } ]"
}
}
FunctionUtilities.cs
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
namespace Rackspace.AzureFunctions
{
public static class FunctionUtilities
{
public class BindingRedirect
{
public string ShortName { get; set; }
public string PublicKeyToken { get; set; }
public string RedirectToVersion { get; set; }
}
public static void ConfigureBindingRedirects()
{
var config = Environment.GetEnvironmentVariable("BindingRedirects");
var redirects = JsonConvert.DeserializeObject<List<BindingRedirect>>(config);
redirects.ForEach(RedirectAssembly);
}
public 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.Version = new Version(bindingRedirect.RedirectToVersion);
requestedAssembly.SetPublicKeyToken(targetPublicKeyToken);
requestedAssembly.CultureInfo = CultureInfo.InvariantCulture;
AppDomain.CurrentDomain.AssemblyResolve -= handler;
return Assembly.Load(requestedAssembly);
};
AppDomain.CurrentDomain.AssemblyResolve += handler;
}
}
}
Just posted a new blog post explaining how to fix the problem, have a look:
https://codopia.wordpress.com/2017/07/21/how-to-fix-the-assembly-binding-redirect-problem-in-azure-functions/
It's actually a tweaked version of the JoeBrockhaus's code, that works well even for Newtonsoft.Json.dll
Inspired by the accepted answer I figured I'd do a more generic one which takes into account upgrades as well.
It fetches all assemblies, orders them descending to get the newest version on top, then returns the newest version on resolve. I call this in a static constructor myself.
public static void RedirectAssembly()
{
var list = AppDomain.CurrentDomain.GetAssemblies()
.Select(a => a.GetName())
.OrderByDescending(a => a.Name)
.ThenByDescending(a => a.Version)
.Select(a => a.FullName)
.ToList();
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var requestedAssembly = new AssemblyName(args.Name);
foreach (string asmName in list)
{
if (asmName.StartsWith(requestedAssembly.Name + ","))
{
return Assembly.Load(asmName);
}
}
return null;
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With