[HowTo] Modificar cadena de conexión, la eterna pregunta
Si no la he leído 200 veces en los foros MSDN no la he leído ninguna 🙂 vayan unos ejemplos:
Es posible modificar el App.config de una aplicación C# en tiempo de ejecución?
Cómo hago para cambiar los datos de conexión en tiempo de ejecución?
Cadena de conexión Dinámica, ¿cómo debo hacer esta modificación desde un windows form en el mismo aplicativo?
Y así unas cuantas. De hecho una búsqueda me ha arrojado más de 3.000 resultados :-S
De todos modos lo que me sorprende no es tanto el alto número de veces que se hace esta pregunta, sino que cada vez que la respondo me digo a mi mismo: Haz un post y publícalo para referenciar a la gente… y siempre se me olvida 😛
Bueno, pues de hoy no pasa. Y es que curiosamente me ha llegado la misma pregunta por dos medios distintos. Uhm… casualidad? No lo creo. Sin duda es una señal divina: El MEV me está hablando y yo -pobre mortal- debo obedecer.
App.config y Machine.config
A ver, el quid de la cuestión es que la primera vez que usamos una cadena de conexión (en un DataSource, Contexto de datos, etc.) el propio Visual Studio genera en el fichero app.config una entrada con nombre para nuestra aplicación. Esta entrada es parecida a esto:
<connectionStrings>
<clear />
<add name="YourApp.Properties.Settings.YourAppConnectionString"
connectionString="Data Source=.sqlexpress;Initial Catalog=YourDataBase;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
Y sirve para que si alguna vez deseamos cambiar el nombre del servidor, o la base de datos, o cualquier otro atributo, lo hagamos en un único sitio.
Antes de seguir un detalle: ¿Os habéis fijado si en vuestro app.config tenéis un clear antes de la cadena de conexión? Si no lo tenéis, os aconsejo añadirlo. Este clear lo que hace es no cargar las cadenas de conexión del fichero machine.config, que es común a todas las aplicaciones.
Y es que cuando se inicia una aplicación de .NET, se cargan las secciones comunes del machine.config y a continuación se añaden las propias de la aplicación definidas en el app.config.
Como por defecto en el machine.config tenemos esto:
<connectionStrings>
<add name="LocalSqlServer" connectionString="data source=.SQLEXPRESS;Integrated Security=SSPI;
AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient"/>
</connectionStrings>
Si no usamos el tag clear, al ejecutar nuestra aplicación tendremos dos cadenas de conexión, y la primera será la del machine.config… y eso no nos gusta 😛
Así que asumiendo que ya habéis agregado el tag clear, vamos al turrón.
System.Configuration
En este ensamblado encontraremos todo lo necesario para la gestión de nuestras cadenas de configuración (y muchas cosas más).
Lo primero es agregar una referencia a este ensamblado a nuestro proyecto:
Y a continuación crearemos una clase con un par de métodos para leer y guardar las cadenas de conexión en el app.config:
public class ConnectionStringManager
{
public static string GetConnectionString(string connectionStringName)
{
Configuration appconfig =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConnectionStringSettings connStringSettings = appconfig.ConnectionStrings.ConnectionStrings[connectionStringName];
return connStringSettings.ConnectionString;
}
public static void SaveConnectionString(string connectionStringName, string connectionString)
{
Configuration appconfig =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
appconfig.ConnectionStrings.ConnectionStrings[connectionStringName].ConnectionString = connectionString;
appconfig.Save();
}
}
Básicamente, al primero se le pasa el nombre de la cadena de conexión a recuperar (por si tenemos varias) y nos devuelve la cadena de conexión. Al segundo se le pasa el nombre de la cadena de conexión y la nueva cadena de conexión modificada.
Para hacerlo un poco más sencillo y no tener que recordar el nombre de las cadenas de conexión, vamos a crear algunos métodos más en la clase. Uno que retorne la lista de nombres de las cadenas de conexión del fichero de configuración, y otro que retorne sólo el nombre de la primera (muy útil si sólo tenemos una).
public static List<string> GetConnectionStringNames()
{
List<string> cns = new List<string>();
Configuration appconfig =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
foreach (ConnectionStringSettings cn in appconfig.ConnectionStrings.ConnectionStrings)
{
cns.Add(cn.Name);
}
return cns;
}
public static string GetFirstConnectionStringName()
{
return GetConnectionStringNames().FirstOrDefault();
}
Incluso, basándonos en este último podemos crear otro que nos devuelva la primera cadena de conexión:
public static string GetFirstConnectionString()
{
return GetConnectionString(GetFirstConnectionStringName());
}
Let’s play!
A partir de esta clase, trabajar con las cadenas de conexión es muy sencillo. Supongamos que tenemos un formulario similar a esto:
Leer los valores de la cadena de conexión y mostrarlos es tan sencillo como esto:
private void showSavedConnectionSettings()
{
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder(ConnectionStringManager.GetFirstConnectionString());
connectionServerComboBox.EditValue = builder.DataSource.ToUpper();
connectionDatabaseComboBox.EditValue = builder.InitialCatalog.ToUpper();
if (builder.IntegratedSecurity)
{
connectionAuthenticationModeRadioGroup.EditValue = 0;
connectionUsernameTextEdit.Text = string.Empty;
connectionPasswordTextEdit.Text = string.Empty;
}
else
{
connectionAuthenticationModeRadioGroup.EditValue = 1;
connectionUsernameTextEdit.Text = builder.UserID;
connectionPasswordTextEdit.Text = builder.Password;
}
}
Es decir, creamos un SqlConnectionStringBuilder a partir de la primera cadena de conexión del fichero de configuración y a partir de aquí, trabajamos por separado con cada una de las partes de la cadena, accediendo a las propiedades del builder (Datasource, InitialCatalog, IntegratedSecurity, etc.). De este modo las mostramos en el form, o dónde queramos…
Guardando que es gerundio
La siguiente pregunta lógica es: ¿Y para guardar sólo uno de los valores en el fichero de configuración? El típico ejemplo es modificar el nombre del servidor al instalar en el cliente final. Para ello vamos a usar también un builder, y vamos a ampliar la clase anterior con algunos métodos más. Para obtener los valores de las propiedades de la cadena de conexión y para guardarlos:
Obtener valores de propiedades de la cadena de conexión:
public static string GetSqlServerServerName()
{
string cs = GetConnectionString(GetFirstConnectionStringName());
if (cs != null)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(cs);
return builder.DataSource;
}
else
return null;
}
public static string GetSqlServerDatabaseName()
{
string cs = GetConnectionString(GetFirstConnectionStringName());
if (cs != null)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(cs);
return builder.InitialCatalog;
}
else
return null;
}
public static string GetSqlServerUserName()
{
string cs = GetConnectionString(GetFirstConnectionStringName());
if (cs != null)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(cs);
return builder.UserID;
}
else
return null;
}
public static string GetSqlServerPassword()
{
string cs = GetConnectionString(GetFirstConnectionStringName());
if (cs != null)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(cs);
return builder.Password;
}
else
return null;
}
public static bool? GetSqlServerIntegratedSecurity()
{
string cs = GetConnectionString(GetFirstConnectionStringName());
if (cs != null)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(cs);
return builder.IntegratedSecurity;
}
else
return null;
}
Guardar valores de propiedades en la cadena de conexión:
public static string SetConnectionStringServerName(
string connectionString, string serverName)
{
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder(connectionString);
builder.DataSource = serverName;
return builder.ConnectionString;
}
public static string SetConnectionStringAutenticationIntegrated(
string connectionString)
{
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder(connectionString);
builder.IntegratedSecurity = true;
return builder.ConnectionString;
}
public static string SetConnectionStringAutenticationSQLServer(
string connectionString, string username, string password)
{
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder(connectionString);
builder.IntegratedSecurity = false;
builder.UserID = username;
builder.Password = password;
return builder.ConnectionString;
}
public static string SetConnectionStringDatabaseName(
string connectionString, string databaseName)
{
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder(connectionString);
builder.InitialCatalog = databaseName;
return builder.ConnectionString;
}
Así pues, para cambiar el nombre del servidor en el app.config bastaría con:
-
Leer la cadena de conexión actual
-
Cambiar el nombre del server
-
Volver a guardar la cadena de conexión
string cs = ConnectionStringManager.GetFirstConnectionString();
string newConStr = ConnectionStringManager.SetConnectionStringServerName(cs, "MyNewServer");
ConnectionStringManager.SaveConnectionString(
ConnectionStringManager.GetFirstConnectionStringName(), newConStr);
Me han quedado bastantes cosas en el tintero (recuperar los servidores SQL Server de nuestra red local, mostrar las bases de datos de un server, o crear un formulario para el usuario final). Si hay tiempo y pensáis que es un tema de interés lo veremos en futuros posts.
Nos vemos! 😀
One Responseso far
Sería interesante ver ese diálogo de Sql Server 🙂
Respecto a cuadros de diálogos así, éstas son buenas referencias:
http://stackoverflow.com/questions/2205993/winforms-connection-properties-dialog-for-configuration-string
Data Connection Dialog
http://archive.msdn.microsoft.com/Connection
Saludos