Azure Functions binding redirect

前端 未结 6 603
南笙
南笙 2020-11-30 04:39

Is it possible to include a web.config or app.config file in the azure functions folder structure to allow assembly binding redirects?

6条回答
  •  無奈伤痛
    2020-11-30 05:13

    First SO post, so apologies if formatting's a bit off.

    We've hit this issue a couple of times and managed to find a better way of getting the required redirects by forcing MSBUILD to generate a binding redirects file and then parsing that to be used with the previously suggested answer.

    Modify the project settings and add in a couple of targets:

    
      
        ...
        True
        true
        ...
      
    
    

    These classes apply the binding redirects using the same idea that was posted earlier (link) except instead of using the host.json file it reads from the generated binding redirects file. The filename to use is from reflection using the 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 BindingRedirects { get; } = new HashSet();
    
            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(fullPath);
                TransformData(xml);
            }
    
            private T ReadFile(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 classes to use to deserialise the generated binding redirect file into something easier to use. These were generated from the binding redirects file by using VS2017 "paste special -> paste xml as classes" so feel free to roll your own if needed.

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

提交回复
热议问题