【问题标题】:How to get full list of CloudConfiguration from inside a web service at runtime?如何在运行时从 Web 服务内部获取 CloudConfiguration 的完整列表?
【发布时间】:2015-09-12 01:31:44
【问题描述】:

ConfigurationManager 具有 AppSettings 名称-值集合,但 CloudConfigurationManager 只有 GetSetting(string) 方法,如果您知道密钥,则可以在其中 1 个 1 获取配置设置。

有没有办法获取角色运行时的整个配置?

根本原因是我想进行强类型配置,以便将其抽象出来并使我的代码更具可测试性。直接使用 CloudConfigurationManager 是隐式依赖项,我想用我想在测试中存根的抽象来删除它。所以我觉得这很实用。这让我想到了我的问题。

我不想使用像 fx.configuration.azure 这样的库,因为我必须完全携带它的依赖项,因为它需要继承基类。

【问题讨论】:

    标签: azure azure-web-roles azure-configuration


    【解决方案1】:

    这是一个更新的实现,它会注意您是否在模拟器中运行以及是否在本地 Web 服务器中运行。返回字典后,Castle.DictionaryAdapter 可以轻松地将其从整个应用程序中抽象出来。我在 GitHub here 上将代码作为模板项目共享。摘录如下:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Security.Cryptography.X509Certificates;
    using System.Xml;
    using System.Xml.Linq;
    using Castle.Components.DictionaryAdapter;
    using Core.Configuration.Interfaces;
    using Microsoft.Azure;
    using Microsoft.WindowsAzure.Management.Compute;
    using Microsoft.WindowsAzure.Management.Compute.Models;
    using Microsoft.WindowsAzure.ServiceRuntime;
    
    namespace Core.Configuration
    {
        public class AzureServiceConfigurationProvider : IAzureServiceConfigurationProvider
        {
            private readonly string _subscriptionId;
            // The Base64 Encoded Management Certificate string from Azure Publish Settings file 
            // download from https://manage.windowsazure.com/publishsettings/index
            private readonly string _managementCertContents;
            private readonly string _cloudServiceName;
            private readonly string _serviceConfigurationNamespace;
    
            public DefaultAzureServiceConfigurationProvider(IWebConfigSettings webConfigSettings)
            {
                _subscriptionId = webConfigSettings.SubscriptionId;
                _managementCertContents = webConfigSettings.ManagementCertContents;
                _cloudServiceName = webConfigSettings.CloudServiceName;
                _serviceConfigurationNamespace = webConfigSettings.ServiceConfigurationNamespace;
            }
    
            public Dictionary<string, Dictionary<string, string>> GetConfigRaw()
            {
                Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->Start");
                var configuration = new Dictionary<string, Dictionary<string, string>>();
                var configXml = GetConfigXml();
                Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->XmlExtracted");
    
                var roles = configXml.Descendants(XName.Get("Role", _serviceConfigurationNamespace));
                Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->Roles : ");
                foreach(var role in roles)
                {
                    var roleConfiguration = new Dictionary<string, string>();
                    var roleName = role.Attribute("name").Value;
                    Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->RoleName : " + roleName);
                    var configurationSettings = role.Element(XName.Get("ConfigurationSettings", _serviceConfigurationNamespace));
    
                    if (configurationSettings == null)
                    {
                        throw new InvalidOperationException("configurationSettings is null");
                    }
    
                    foreach(var element in configurationSettings.Elements(XName.Get("Setting", _serviceConfigurationNamespace)))
                    {
    
                        var settingName = element.Attribute("name").Value;
                        var settingValue = element.Attribute("value").Value;
                        Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigRaw->settingName : " + settingName + " settingValue : " + settingValue);
                        roleConfiguration.Add(settingName, settingValue);
                    }
                    configuration.Add(roleName, roleConfiguration);
                }
                return configuration;
            }
    
            public IAzureServiceConfiguration GetConfig()
            {
                var configFactory = new DictionaryAdapterFactory();
                IAzureServiceConfiguration config;
                try
                {
    
                    var rawAzureServiceConfig = GetConfigRaw();
                    Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfig :");
                    var rawAzureWebServiceConfig = rawAzureServiceConfig["Core.Web"];
                    config = configFactory.GetAdapter<IAzureServiceConfiguration>(rawAzureWebServiceConfig);
                    config = ComplementConfigurationFromConfigurationManager(config);
                }
                catch(Exception exception)
                {
                    // happens in some projects when using Full Emulator
                    // so we fallback to cloudconfigurationmanager
                    // this is not bad since we have isolated it in configuration assembly
    
                    Trace.WriteLine(exception.Message);
                    Trace.WriteLine(exception.StackTrace);
                    Hashtable hashConfig = GetConfigFromConfigurationManager();
                    config = configFactory.GetAdapter<IAzureServiceConfiguration>(hashConfig);
                }
    
                return config;
            }
    
            private IAzureServiceConfiguration ComplementConfigurationFromConfigurationManager(IAzureServiceConfiguration config)
            {
                Trace.WriteLine("Complementing configuration");
                var azureConfigType = config.GetType();
                foreach(PropertyInfo property in config.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly))
                {
                    var xmlConfigValue = CloudConfigurationManager.GetSetting(property.Name);
    
                    var liveConfigPropValue = (string)azureConfigType.GetProperty(property.Name).GetValue(config, null);
    
                    if(string.IsNullOrEmpty(liveConfigPropValue))
                    {
                        Trace.WriteLine(property.Name + " in live config is empty. Complementing with '" + xmlConfigValue + "' from ConfigurationManager.");
                        property.SetValue(config, xmlConfigValue);
                    }
                    // do something with the property
                }
    
                return config;
            }
    
    
    
            private Hashtable GetConfigFromConfigurationManager()
            {
                Hashtable hashConfig = new Hashtable();
                var configProperties = typeof(IAzureServiceConfiguration).GetProperties();
    
                foreach(PropertyInfo prop in configProperties)
                {
                    hashConfig.Add(prop.Name, CloudConfigurationManager.GetSetting(prop.Name));
                }
                return hashConfig;
            }
    
            private XElement GetConfigXml()
            {
                XElement configXml = null;
                Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml");
                if(!RoleEnvironment.IsAvailable/*as local web project*/ || RoleEnvironment.IsEmulated /*as azure emulator project*/)
                {
                    Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml->!RoleEnvironment.IsAvailable || RoleEnvironment.IsEmulated");
                    try
                    {
                        var localConfigFile =
                            new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory).Parent.EnumerateFiles(
                                "*Local.cscfg", SearchOption.AllDirectories).FirstOrDefault();
                        XmlDocument doc = new XmlDocument();
                        doc.Load(localConfigFile.FullName);
                        configXml = XElement.Parse(doc.InnerXml);
                    }
                    catch(Exception exception) // happens in some projects when using Full Emulator
                    {
                        Trace.WriteLine(exception.Message);
                        Trace.WriteLine(exception.StackTrace);
                        throw; // intended - just marking - will catch it above
                    }
                }
                else
                {
                    Trace.WriteLine("DefaultAzureServiceConfigurationProvider->GetConfigXml->RoleEnvironment ->in cloud");
                    var managementCertificate = new X509Certificate2(Convert.FromBase64String(_managementCertContents));
                    var credentials = new CertificateCloudCredentials(_subscriptionId, managementCertificate);
    
                    var computeManagementClient = new ComputeManagementClient(credentials);
                    var response = computeManagementClient.HostedServices.GetDetailed(_cloudServiceName);
                    var deployment = response.Deployments.FirstOrDefault(d => d.DeploymentSlot == DeploymentSlot.Production);
                    if(deployment != null)
                    {
                        var config = deployment.Configuration;
                        configXml = XElement.Parse(config);
                    }
                }
                return configXml;
            }
    
    
        }
    
        internal static class TypeHelpers
        {
            public static bool IsNumber(this object value)
            {
                return value is sbyte
                        || value is byte
                        || value is short
                        || value is ushort
                        || value is int
                        || value is uint
                        || value is long
                        || value is ulong
                        || value is float
                        || value is double
                        || value is decimal;
            }
    
            public static bool IsString(this object value)
            {
                return value is string;
            }
    
        }
    }
    

    【讨论】:

      【解决方案2】:

      AFAIK,没有直接的方法可以为您提供此信息。

      但是,您可以使用一种解决方法。它涉及使用服务管理 API 的Get Deployment 操作。此操作将返回一个 XML,其中一个元素是 Configuration,其中包含 Base64 编码格式的服务配置文件。您可以读取此元素,将其转换为字符串并解析 XML 以获取 ConfigurationSettings 元素。它的子元素包含所有设置。

      为此,您可以在服务管理 REST API 上编写自己的包装器,也可以使用 Azure Management Library

      更新

      下面是一个示例代码,用于列出来自Service Configuration File 的所有配置设置,使用Azure Management Library。这是一个简单的控制台应用程序,在很短的时间内组合在一起,因此有很大的改进空间:)。对于管理证书,我使用了 Publish Setting File 中的数据。

      您只需在控制台应用程序中安装Azure Management Library Nuget 包:

      安装包 Microsoft.WindowsAzure.Management.Libraries

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using Microsoft.WindowsAzure;
      using Microsoft.WindowsAzure.Management.Compute;
      using System.Security.Cryptography.X509Certificates;
      using System.Xml.Linq;
      
      namespace ReadConfigurationSettingsUsingAzureManagementLibrary
      {
          class Program
          {
              static string subscriptionId = "<subscription-id>";
              static string managementCertContents = "<Base64 Encoded Management Certificate String from Publish Setting File>";//Certificate string from Azure Publish Settings file
              static string cloudServiceName = "<your cloud service name>";
              static string ns = "http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration";
              static void Main(string[] args)
              {
                  var managementCetificate = new X509Certificate2(Convert.FromBase64String(managementCertContents));
                  var credentials = new CertificateCloudCredentials(subscriptionId, managementCetificate);
      
                  var computeManagementClient = new ComputeManagementClient(credentials);
                  var response = computeManagementClient.HostedServices.GetDetailed(cloudServiceName);
                  var deployment = response.Deployments.FirstOrDefault(d => d.DeploymentSlot == Microsoft.WindowsAzure.Management.Compute.Models.DeploymentSlot.Production);
                  if (deployment != null)
                  {
                      var config = deployment.Configuration;
                      XElement configXml = XElement.Parse(config);
                      var roles = configXml.Descendants(XName.Get("Role", ns));
                      foreach (var role in roles)
                      {
                          Console.WriteLine(role.Attribute("name").Value);
                          Console.WriteLine("-----------------------------");
                          var configurationSettings = role.Element(XName.Get("ConfigurationSettings", ns));
                          foreach (var element in configurationSettings.Elements(XName.Get("Setting", ns)))
                          {
                              var settingName = element.Attribute("name").Value;
                              var settingValue = element.Attribute("value").Value;
                              Console.WriteLine(string.Format("{0} = {1}", settingName, settingValue));
                          }
                          Console.WriteLine("==========================================");
                      }
                  }
                  Console.ReadLine();
              }
          }
      }
      

      【讨论】:

      • 我会这么认为。让我尝试写一些代码并分享它。
      • 更新了我的实现并根据您的代码共享了一个 github 项目。谢谢。
      猜你喜欢
      • 2018-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-07
      • 1970-01-01
      • 2011-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多