17 votes

Redirection de la liaison Azure Functions

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 ?

16voto

JoeBrockhaus Points 482

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;
            }
        }
    }

13voto

akazemis Points 1

Je viens de publier un nouvel article de blog expliquant comment résoudre le problème, jetez-y un œil :

https://codopia.wordpress.com/2017/07/21/how-to-fix-the-assembly-binding-redirect-problem-in-azure-functions/

Il s'agit en fait d'une version modifiée du code de JoeBrockhaus, qui fonctionne bien même pour Newtonsoft.Json.dll.

3voto

Mikael Svenson Points 18243

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;
    };
}

2voto

David Ebbo Points 13246

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 !

0voto

Eric Jorgensen Points 29

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;
        };
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X