Mark Wagoner
Mark Wagoner

Reputation: 1769

Entity Framework not using updated connection string

Is it possible to change the connection string within the app.config file and have Entity Framework start using the new values?

I have a multi-threaded app with one thread acting as the dispatcher. This thread has the ability to receive a message telling it to change all or part of the connection string data (such as a password change). It then updates the config file to include the new connection information and clears any pooled database connections. This part is all working correctly.

The problem is, when the dispatcher launches a new thread and this thread creates a new database context the database connection is created with the old connection string values. It appears Entity Framework is keeping a cached copy of this information somewhere. If I completely shutdown and restart the app it works correctly because it, obviously, has to read the new values from the config file.

I have seen other proposed solutions involving keeping the connection string as a variable and passing it to the context constructor. But since the dispatcher thread has no internal knowledge of the "job" threads it creates (some may not even require database access) this won't really work for me. At least not without a lot of code changes and special requirements of my job interface.

This is the code I'm using to update the file. As I said, this part is working. Is there something else I need to do to tell Entity Framework about the change?

 // Create new connection string

 SqlConnectionStringBuilder connectionBuilder = new SqlConnectionStringBuilder(ConfigurationManager.ConnectionStrings[CONNECTION_STRING_NAME].ConnectionString);
 connectionBuilder.DataSource = server;
 connectionBuilder.InitialCatalog = database;
 connectionBuilder.UserID = user;
 connectionBuilder.Password = password;

 // Update connection string within config file

 Configuration config = ConfigurationManager.OpenExeConfiguration(Assembly.GetExecutingAssembly().Location);
 ConnectionStringsSection section = (ConnectionStringsSection)config.GetSection("connectionStrings");
 section.ConnectionStrings[CONNECTION_STRING_NAME].ConnectionString = connectionBuilder.ToString();

 // Save changes

 ConfigurationManager.RefreshSection("connectionStrings");
 config.Save();

Upvotes: 2

Views: 1116

Answers (1)

Ivan Stoev
Ivan Stoev

Reputation: 205629

The documentation for ConfigurationManager.RefreshSection method does say (emphasis is mine):

Refreshes the named section so the next time that it is retrieved it will be re-read from disk.

The problem is that EF6 caches the ConfigurationManager.ConnectionStrings in an internal class static property, so it never gets refreshed.

Unfortunately there is no "official" way to refresh it. Following is a brute force hack using reflection, so use it on your own risk:

static readonly Type EF6AppConfigType = typeof(DbContext).Assembly.GetType("System.Data.Entity.Internal.AppConfig");

static readonly PropertyInfo EF6DefaultAppConfig = EF6AppConfigType.GetProperty("DefaultInstance", BindingFlags.Public | BindingFlags.Static);

static readonly FieldInfo EF6ConnectionStrings = EF6AppConfigType.GetField("_connectionStrings", BindingFlags.NonPublic | BindingFlags.Instance);

static void RefreshEF6ConnectionStrings()
{
    EF6ConnectionStrings.SetValue(
        EF6DefaultAppConfig.GetValue(null),
        ConfigurationManager.ConnectionStrings
    );
}

Just call it after saving the changes:

config.Save();
RefreshEF6ConnectionStrings();

Upvotes: 3

Related Questions