【问题标题】:Package and publish Azure Cloud Service with unique Sql Azure connectionStrings使用唯一的 Sql Azure 连接字符串打包和发布 Azure 云服务
【发布时间】:2013-04-16 14:38:02
【问题描述】:

我有一个带有 Azure 云服务项目和单个 webrole 的 .NET Web 解决方案。我将它部署到东海岸西海岸数据/计算中心以进行故障转移,并被要求使用 Powershell MSBuild 和 Jenkins 自动部署。

问题是我需要在打包和发布到每个部署之前更改 Web.config 中的 Sql Azure 数据库连接字符串。看起来很简单。

我了解 webrole 属性设置选项卡允许您使用“字符串”或“连接字符串”类型向每个部署添加自定义配置属性,但看起来“连接字符串”选项仅适用于 Blob、表或队列存储。如果我使用“字符串”并给它一个 Sql Azure 连接字符串类型,它会将其写为键/值对,而 Entity Framework 和 Membership Provider 找不到它。

有没有办法添加指向 Sql Azure 的每个部署的连接字符串设置?

谢谢,

大卫

【问题讨论】:

    标签: sql azure connection-string


    【解决方案1】:

    Erick 的解决方案是完全有效的,我找到了另一种解决问题的方法,所以我想我会回来把它放在这里,因为我很难找到答案。

    诀窍是让实体框架和任何提供程序(如 asp.net Membership/profile/session 等)直接从 Azure 服务配置而不是站点 web.config 文件中读取连接字符串。

    对于提供程序,我能够创建继承 System.Web.Providers.DefaultMembershipProvider 类并覆盖 Initialize() 方法的类,然后我使用我编写的帮助程序类使用 RoleEnvironment.GetConfigurationSettingValue(设置名称);调用,它从 Azure 服务配置中读取。

    然后我告诉成员资格提供者使用我的类而不是 DefaultMembershipProvider。代码如下:

    Web.config:

    <membership defaultProvider="AzureMembershipProvider">
      <providers>
        <add name="AzureMembershipProvider" type="clientspace.ServiceConfig.AzureMembershipProvider" connectionStringName="ClientspaceDbContext" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
      </providers>
    

    注意自定义提供程序“AzuremembershipProvider”

    AzuremembershipProvider 类:

    public class AzureMembershipProvider : System.Web.Providers.DefaultMembershipProvider
    {
        public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
        {         
            string connectionStringName = config["connectionStringName"];
    
            AzureProvidersHelper.UpdateConnectionString(connectionStringName, AzureProvidersHelper.GetRoleEnvironmentSetting(connectionStringName), 
                AzureProvidersHelper.GetRoleEnvironmentSetting(connectionStringName + "ProviderName"));
    
            base.Initialize(name, config);
        }
    }
    

    这里是帮助类 AzureProvidersHelper.cs:

    public static class AzureProvidersHelper
    {
        internal static string GetRoleEnvironmentSetting(string settingName)
        {
            try
            {
                return RoleEnvironment.GetConfigurationSettingValue(settingName);
            }
            catch
            {
                throw new ConfigurationErrorsException(String.Format("Unable to find setting in ServiceConfiguration.cscfg: {0}", settingName));
            }
        }
    
        private static void SetConnectionStringsReadOnly(bool isReadOnly)
        {
            var fieldInfo = typeof (ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
            if (
                fieldInfo != null)
                fieldInfo.SetValue(ConfigurationManager.ConnectionStrings, isReadOnly);
        }
    
        private static readonly object ConnectionStringLock = new object();
    
        internal static void UpdateConnectionString(string name, string connectionString, string providerName)
        {
            SetConnectionStringsReadOnly(false);
    
            lock (ConnectionStringLock)
            {
                ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings["name"];
                if (connectionStringSettings != null)
                {
                    connectionStringSettings.ConnectionString = connectionString;
                    connectionStringSettings.ProviderName = providerName;
                }
                else
                {
                    ConfigurationManager.ConnectionStrings.Add(new ConnectionStringSettings(name, connectionString, providerName));
                }
            }
    
            SetConnectionStringsReadOnly(true);
        }
    }
    

    这里的关键是 RoleEnvironment.GetConfigurationSettingValue 从 Azure 服务配置而不是 web.config 读取。

    对于未指定提供程序的实体框架,我必须再次使用帮助程序类中的 GetRoleEnvironmentSetting() 方法将此调用添加到 Global.asax:

    var connString = AzureProvidersHelper.GetRoleEnvironmentSetting("ClientspaceDbContext");
    
            Database.DefaultConnectionFactory = new SqlConnectionFactory(connString);
    

    这个解决方案的好处是您最终不必处理 Azure 角色 onstart 事件。

    享受

    dash

    【讨论】:

      【解决方案2】:

      大卫,

      一个不错的选择是使用 Azure 配置。如果你右键单击 Azure 项目,你可以添加一个额外的配置。将您的连接字符串放入正确的配置中(例如,ServiceConfiguration.WestCoast.cscfg、ServiceConfiguration.EastCoast.cscfg 等)。

      在您的构建脚本中,使用配置名称将 TargetProfile 属性传递给 MSBuild,这些设置将构建到最终的 cscfg 中。

      如果您遇到任何问题,请告诉我。我采用了这种方法,并且尝试了几次才能使其正常工作。 Some details that might help.

      埃里克

      【讨论】:

      • 埃里克,感谢您的回复。 5 分钟前,我实际上已经将页面放在我面前。为了清楚起见,我有 3 个 *.cscfg 配置(东、西、QA),但实体框架和成员资格提供者希望在 web.config 的 部分中指定一个与名称匹配的连接我的 DbContext 类并将字符串放在 *.cscfg 中似乎将它们作为键/值对添加到配置的 部分中。因此 EF 和 MemProvider 找不到它们。你是说你用这种方法解决了这个问题?
      • 那种。我所做的是使用 .cscfg 中的值更新角色开始时的 web.config。见stackoverflow.com/questions/16042212/…
      • 另一个选项是使用 web.config 转换。它使您可以更好地控制对 web.config 文件的更新,但我发现在构建服务器上使用它更难。但是,如果您需要更多功率,这是一个不错的选择。 msdn.microsoft.com/en-us/library/dd465318(v=vs.100).aspx
      猜你喜欢
      • 2015-08-10
      • 1970-01-01
      • 1970-01-01
      • 2014-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-07
      相关资源
      最近更新 更多