Est-il possible d'inclure un fichier web.config ou app.config dans la structure de dossiers des fonctions azur pour permettre les redirections de liaison d'assemblage ?
Réponses
Trop de publicités?En supposant que vous utilisez la dernière version (juin 17) de Visual Studio 2017 Function Tooling, j'ai trouvé une solution quelque peu raisonnable basée sur la configuration en suivant un extrait de code publié par npiasecki
sur Numéro 992 .
L'idéal serait que cela soit géré par le cadre de travail, mais au moins, en étant axé sur la configuration, vous avez un peu plus d'isolation des changements. Je suppose que vous pourriez également utiliser des étapes de pré-construction ou des modèles T4 qui réconcilient les versions des nugets dans le projet (et leurs dépendances) avant d'écrire cette configuration ou de générer du code.
Donc le revers de la médaille..
devient de devoir se rappeler de mettre à jour le BindingRedirects
lorsque vous mettez à jour le paquet NuGet ( c'est souvent un problème dans les app.configs de toute façon ). Vous pouvez également avoir un problème avec la solution pilotée par la configuration si vous devez rediriger Newtonsoft
.
Dans notre cas, nous utilisions le nouveau NuGet Azure Fluent qui avait une dépendance sur une ancienne version de Microsoft.IdentityModel.Clients.ActiveDirectory
que la version des bibliothèques de gestion ARM normales qui sont utilisées côte à côte dans une fonction particulière.
local.settings.json
{
"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;
}
}
}
Inspiré par la réponse acceptée, je me suis dit que j'allais en faire une plus générique qui prendrait également en compte les mises à niveau.
Il récupère tous les assemblages, les ordonne par ordre décroissant pour obtenir la version la plus récente en haut, puis renvoie la version la plus récente sur resolve. Je l'appelle moi-même dans un constructeur statique.
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;
};
}
Ce n'est pas directement possible aujourd'hui, mais nous réfléchissons aux moyens d'y parvenir. Pouvez-vous s'il vous plaît ouvrir un problème sur https://github.com/Azure/azure-webjobs-sdk-script/issues pour s'assurer que votre scénario spécifique est examiné ? Merci !
Voici une solution alternative lorsque vous souhaitez obtenir la version exacte d'un assemblage particulier. Avec ce code, vous pouvez facilement déployer les assemblages qui sont manquants :
public static class AssemblyHelper
{
//--------------------------------------------------------------------------------
/// <summary>
/// Redirection hack because Azure functions don't support it.
/// How to use:
/// If you get an error that a certain version of a dll can't be found:
/// 1) deploy that particular dll in any project subfolder
/// 2) In your azure function static constructor, Call
/// AssemblyHelper.IncludeSupplementalDllsWhenBinding()
///
/// This will hook the binding calls and look for a matching dll anywhere
/// in the $HOME folder tree.
/// </summary>
//--------------------------------------------------------------------------------
public static void IncludeSupplementalDllsWhenBinding()
{
var searching = false;
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
// This prevents a stack overflow
if(searching) return null;
var requestedAssembly = new AssemblyName(args.Name);
searching = true;
Assembly foundAssembly = null;
try
{
foundAssembly = Assembly.Load(requestedAssembly);
}
catch(Exception e)
{
Debug.WriteLine($"Could not load assembly: {args.Name} because {e.Message}");
}
searching = false;
if(foundAssembly == null)
{
var home = Environment.GetEnvironmentVariable("HOME") ?? ".";
var possibleFiles = Directory.GetFiles(home, requestedAssembly.Name + ".dll", SearchOption.AllDirectories);
foreach (var file in possibleFiles)
{
var possibleAssembly = AssemblyName.GetAssemblyName(file);
if (possibleAssembly.Version == requestedAssembly.Version)
{
foundAssembly = Assembly.Load(possibleAssembly);
break;
}
}
}
return foundAssembly;
};
}
}