it-swarm.com.ru

Перенаправление привязки функций Azure

Можно ли включить файл web.config или app.config в структуру папок функций Azure, чтобы разрешить перенаправления привязки сборки?

18
Martin Smyllie

Только что опубликовал новое сообщение в блоге, объясняющее, как решить проблему, посмотрите:

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

На самом деле это измененная версия кода JoeBrockhaus, которая хорошо работает даже для Newtonsoft.Json.dll

16
akazemis

Вдохновленный принятым ответом, я решил, что сделаю более общий ответ, который также учитывает улучшения.

Он выбирает все сборки, упорядочивает их по убыванию, чтобы получить самую новую версию сверху, а затем возвращает самую новую версию при разрешении. Я называю это в статическом конструкторе сам.

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;
    };
}
4
Mikael Svenson

Сегодня это невозможно напрямую, но мы думаем о том, как этого добиться. Не могли бы вы открыть вопрос по https://github.com/Azure/azure-webjobs-sdk-script/issues , чтобы убедиться, что ваш конкретный сценарий рассматривается? Спасибо!

2
David Ebbo

Вот альтернативное решение, когда вам нужна точная версия конкретной сборки. С помощью этого кода вы можете легко развернуть отсутствующие сборки:

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;
        };
    }
}
0
Eric Jorgensen

Первая запись SO, поэтому извиняюсь за форматирование.

Мы несколько раз сталкивались с этой проблемой и смогли найти лучший способ получения требуемых перенаправлений, заставив MSBUILD создать файл перенаправлений привязки, а затем проанализировать его для использования с ранее предложенным ответом.

Измените настройки проекта и добавьте пару целей:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    ...
    <AutoGenerateBindingRedirects>True</AutoGenerateBindingRedirects>
    <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
    ...
  </PropertyGroup>
</Project>

Эти классы применяют перенаправления привязки, используя ту же идею, которая была опубликована ранее ( link ), за исключением того, что вместо использования файла Host.json, который он считывает из сгенерированного файла перенаправлений привязки. Имя файла для использования от отражения с использованием ExecutingAssembly.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;

 public static class AssemblyBindingRedirectHelper
    {
        private static FunctionRedirectBindings _redirects;

        public static void ConfigureBindingRedirects()
        {
            // Only load the binding redirects once
            if (_redirects != null)
                return;

            _redirects = new FunctionRedirectBindings();

            foreach (var redirect in _redirects.BindingRedirects)
            {
                RedirectAssembly(redirect);
            }
        }

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

    public class FunctionRedirectBindings
    {
        public HashSet<BindingRedirect> BindingRedirects { get; } = new HashSet<BindingRedirect>();

        public FunctionRedirectBindings()
        {
            var assm = Assembly.GetExecutingAssembly();
            var bindingRedirectFileName = $"{assm.GetName().Name}.dll.config";
            var dir = Path.Combine(Environment.GetEnvironmentVariable("HOME"), @"site\wwwroot");
            var fullPath = Path.Combine(dir, bindingRedirectFileName);

            if(!File.Exists(fullPath))
                throw new ArgumentException($"Could not find binding redirect file. Path:{fullPath}");

            var xml = ReadFile<configuration>(fullPath);
            TransformData(xml);
        }

        private T ReadFile<T>(string path)
        {
            using (StreamReader reader = new StreamReader(path))
            {
                var serializer = new XmlSerializer(typeof(T));
                var obj = (T)serializer.Deserialize(reader);
                reader.Close();
                return obj;
            }
        }

        private void TransformData(configuration xml)
        {
            foreach(var item in xml.runtime)
            {
                var br = new BindingRedirect
                {
                    ShortName = item.dependentAssembly.assemblyIdentity.name,
                    PublicKeyToken = item.dependentAssembly.assemblyIdentity.publicKeyToken,
                    RedirectToVersion = item.dependentAssembly.bindingRedirect.newVersion
                };
                BindingRedirects.Add(br);
            }
        }
    }

    public class BindingRedirect
    {
        public string ShortName { get; set; }
        public string PublicKeyToken { get; set; }
        public string RedirectToVersion { get; set; }
    }

Классы XML, используемые для десериализации сгенерированного файла перенаправления привязки во что-то более простое в использовании. Они были сгенерированы из файла перенаправлений привязки с использованием VS2017 "Вставить специальные -> Вставить xml как классы", поэтому при необходимости вы можете свободно свернуть свои собственные.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Xml.Serialization;

// NOTE: Generated code may require at least .NET Framework 4.5 or .NET Core/Standard 2.0.
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class configuration
{

    [System.Xml.Serialization.XmlArrayItemAttribute("assemblyBinding", Namespace = "urn:schemas-Microsoft-com:asm.v1", IsNullable = false)]
    public assemblyBinding[] runtime { get; set; }
}

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-Microsoft-com:asm.v1")]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "urn:schemas-Microsoft-com:asm.v1", IsNullable = false)]
public partial class assemblyBinding
{

    public assemblyBindingDependentAssembly dependentAssembly { get; set; }
}

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-Microsoft-com:asm.v1")]
public partial class assemblyBindingDependentAssembly
{

    public assemblyBindingDependentAssemblyAssemblyIdentity assemblyIdentity { get; set; }

    public assemblyBindingDependentAssemblyBindingRedirect bindingRedirect { get; set; }
}

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-Microsoft-com:asm.v1")]
public partial class assemblyBindingDependentAssemblyAssemblyIdentity
{

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string name { get; set; }

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string publicKeyToken { get; set; }

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string culture { get; set; }
}

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true, Namespace = "urn:schemas-Microsoft-com:asm.v1")]
public partial class assemblyBindingDependentAssemblyBindingRedirect
{

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string oldVersion { get; set; }

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string newVersion { get; set; }
}
0
xsneebsx