Generadores de cadenas de conexión en Microsoft.Data.SqlClient

Usa SqlConnectionStringBuilder para crear y editar cadenas de conexión mediante propiedades tipadas. Hereda de DbConnectionStringBuilder. Úsala siempre que tu código añada o cambie un valor, en lugar de unir cadenas.

El constructor:

  • Expone cada palabra clave como una propiedad tipada, como ConnectTimeoutun int y Encrypt como un SqlConnectionEncryptOption.
  • Rechaza palabras clave desconocidas y muchos valores fuera de rango con ArgumentException cuando los configuras.
  • Mapea sinónimos como Server y uid a palabras clave canónicas como Data Source y User ID.
  • Pone entre comillas los valores que contienen puntos y coma, comillas o espacios iniciales o finales.

Para la lista de palabras clave, consulta Opciones de conexión. Para el formato de texto, véase Reglas de análisis sintáctico.

Cada muestra es un programa completo. Las pestañas del .NET Framework usan C# 7.3. Para crear un proyecto que ejecute las muestras, consulta Crear el proyecto.

Prevenir la inyección de cadena de conexión

Cuando tu código inserta texto no confiable en una cadena de conexión, un punto y coma en ese texto puede añadir una palabra clave o reemplazar una anterior, porque gana el último valor. El generador almacena todo el valor en una sola propiedad y lo pone entre comillas, así que el texto no puede convertirse en otra palabra clave.

Este programa establece InitialCatalog a un valor que intenta desactivar el pooling:

using Microsoft.Data.SqlClient;

// Simulates a value that came from a user or another untrusted source.
string databaseFromUser = "Orders;Pooling=false";

var builder = new SqlConnectionStringBuilder
{
    DataSource = "tcp:sql.example.com,1433",
    InitialCatalog = databaseFromUser,
    IntegratedSecurity = true,
    Encrypt = SqlConnectionEncryptOption.Strict,
};

Console.WriteLine(builder.ConnectionString);
Console.WriteLine($"Pooling: {builder.Pooling}");

La salida es la siguiente:

Data Source=tcp:sql.example.com,1433;Initial Catalog="Orders;Pooling=false";Integrated Security=True;Encrypt=Strict
Pooling: True

El generador indica el valor y Pooling se mantiene True. El controlador envía Orders;Pooling=false al servidor como nombre de la base de datos.

Poner entre comillas detiene la inyección de palabras clave. No determina si un valor está permitido. Comprueba la entrada no confiable frente a una lista de servidores o bases de datos permitidos antes de usarla.

Completar una cadena de conexión base

Guarda las partes de una cadena de conexión que no cambian, como el servidor y la base de datos, en configuración. Añade el secreto y los valores específicos de la carga de trabajo en tiempo de ejecución. Pasa la cadena base al constructor del generador y luego establece propiedades.

Este programa lee una cadena base y una contraseña de variables de entorno separadas, se conecta con autenticación SQL y confirma que la contraseña no es visible después de que se abre la conexión.

using Microsoft.Data.SqlClient;

// The base string holds no secret. The password comes from a separate secret source.
string baseConnectionString =
    Environment.GetEnvironmentVariable("SQL_BASE_CONNECTION_STRING")
    ?? throw new InvalidOperationException(
        "Set the SQL_BASE_CONNECTION_STRING environment variable.");
string password =
    Environment.GetEnvironmentVariable("SQL_PASSWORD")
    ?? throw new InvalidOperationException(
        "Set the SQL_PASSWORD environment variable.");

var builder = new SqlConnectionStringBuilder(baseConnectionString)
{
    Password = password,
    ApplicationName = "Orders.Worker",
};

await using var connection = new SqlConnection(builder.ConnectionString);
await connection.OpenAsync();

await using var command = new SqlCommand("SELECT APP_NAME();", connection);
Console.WriteLine($"APP_NAME(): {await command.ExecuteScalarAsync()}");

var opened = new SqlConnectionStringBuilder(connection.ConnectionString);
Console.WriteLine($"Password visible after open: {opened.ContainsKey("Password") && opened.Password.Length > 0}");

Establece SQL_BASE_CONNECTION_STRING en un valor como Server=tcp:<server>,1433;Database=<database>;User ID=<user>;Encrypt=true. La salida es la siguiente:

APP_NAME(): Orders.Worker
Password visible after open: False

Persist Security Info el valor predeterminado es false, así que el controlador elimina la contraseña de SqlConnection.ConnectionString después de que se abra la conexión. Conserva ese valor por defecto.

Prefiera un método de autenticación que no necesite contraseña, como la autenticación integrada de Windows o una identidad administrada de Microsoft Entra ID. Para más información, consulte la autenticación de Microsoft Entra ID y Proteger la información de conexión.

Leer y cambiar valores

El builder también funciona como diccionario. El indexador, ContainsKey, Remove, y TryGetValue aceptan una palabra clave o cualquiera de sus sinónimos.

using Microsoft.Data.SqlClient;

var b = new SqlConnectionStringBuilder("Server=sql.example.com;Database=Orders;uid=app_user;pwd=x");
b["server"] = "tcp:sql2.example.com,1433";
Console.WriteLine($"DataSource: {b.DataSource}");
Console.WriteLine($"ContainsKey(\"uid\"): {b.ContainsKey("uid")}");
Console.WriteLine($"Remove(\"pwd\"): {b.Remove("pwd")}");
Console.WriteLine($"TryGetValue: {b.TryGetValue("Database", out object? db)} {db}");
Console.WriteLine($"ShouldSerialize(\"Pooling\"): {b.ShouldSerialize("Pooling")}");
Console.WriteLine(b.ConnectionString);
try { b["Bogus"] = 1; } catch (Exception e) { Console.WriteLine($"{e.GetType().Name}: {e.Message}"); }
try { b.ConnectTimeout = -1; } catch (Exception e) { Console.WriteLine($"{e.GetType().Name}: {e.Message}"); }

La salida es la siguiente:

DataSource: tcp:sql2.example.com,1433
ContainsKey("uid"): True
Remove("pwd"): True
TryGetValue: True Orders
ShouldSerialize("Pooling"): False
Data Source=tcp:sql2.example.com,1433;Initial Catalog=Orders;User ID=app_user
ArgumentException: Keyword not supported: 'Bogus'.
ArgumentException: Invalid value for key 'Connect Timeout'.

ShouldSerialize devuelve false para una palabra clave que no está en la cadena. ContainsKey retorna true para cada palabra clave soportada, independientemente de si la cadena la establece o no. La propiedad ConnectionString solo incluye las palabras clave que estableces.

Entiende lo que el generador no revisa

El constructor rechaza palabras clave desconocidas y muchos valores inválidos, pero no comprueba todos los valores. Por ejemplo, acepta cualquier texto para Type System Version y Transaction Binding, y no comprueba la longitud de las cadenas. Tampoco comprueba combinaciones de palabras clave, por lo que acepta Authentication=Active Directory Default junto con Integrated Security=true. SqlConnection rechaza esa combinación con ArgumentException cuando se le pasa la cadena. Para la lista completa de reglas, véase Reglas de validación.

El generador escribe valores Authentication sin espacios, como Authentication=ActiveDirectoryDefault. El controlador acepta ambas formas.

El creador tampoco protege una contraseña después de que se introduzca en la memoria del proceso, y no decide si un servidor, una identidad o una configuración de certificado es segura.

Mantén una cadena de conexión canónica

Cada cadena de conexión distinta tiene su propio pool de conexiones. Dos cadenas escritas a mano que solo difieren en orden de palabras clave o sinónimos crean dos pools. El constructor escribe palabras clave canónicas en un orden consistente, de modo que Server=a;Database=d y Database=d;Server=a ambos se convierten en Data Source=a;Initial Catalog=d. Construye la cadena de conexión una vez al inicio y reutilízalabuilder.ConnectionString. No añadas valores por solicitud, como nombres de usuario o IDs de solicitud. Para más orientación sobre pooling, consulta Usar pooling de conexiones.