Erick's solution is completely valid and I found an additional way to solve the problem so I thought I'd come back and put it up here since I had such trouble finding an answer.
The trick is getting the Entity Framework and any providers like asp.net Membership/profile/session etc... to read the connection string directly from the Azure service configuration rather than the sites web.config file.
For the providers I was able to create classes that inherit the System.Web.Providers.DefaultMembershipProvider class and override the Initialize() method where I then used a helper class I wrote to retrieve the connection string using the RoleEnvironment.GetConfigurationSettingValue(settingName); call, which reads from the Azure service config.
I then tell the Membership provider to use my class rather than the DefaultMembershipProvider. Here is the code:
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>
Note the custom provider "AzuremembershipProvider"
AzuremembershipProvider class:
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);
}
}
And here's the helper class 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);
}
}
The key here is that the RoleEnvironment.GetConfigurationSettingValue reads from the Azure service configuration and not the web.config.
For the Entity Framework that does not specify a provider I had to add this call to the Global.asax once again using the GetRoleEnvironmentSetting() method from the helper class:
var connString = AzureProvidersHelper.GetRoleEnvironmentSetting("ClientspaceDbContext");
Database.DefaultConnectionFactory = new SqlConnectionFactory(connString);
The nice thing about this solution is that you do not end up having to deal with the Azure role onstart event.
Enjoy
dnash