如何在运行时从Web服务内部获取CloudConfiguration的完整列表?

时间:2015-06-25 12:28:31

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

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

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

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

我不想像fx.configuration.azure那样使用库,因为我需要完全依赖它,因为它需要继承基类。

2 个答案:

答案 0 :(得分:1)

AFAIK,没有直接的方法可以提供这些信息。

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

为此,您可以通过Service Management REST API编写自己的包装器,也可以使用Azure Management Library

<强>更新

以下是使用Azure Management Library列出Service Configuration File所有配置设置的示例代码。这是一个简单的控制台应用程序,在很短的时间内被黑客攻击,因此有很大的改进范围:)。对于管理证书,我使用了发布设置文件中的数据。

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

  

安装包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();
        }
    }
}

答案 1 :(得分: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;
        }

    }
}