Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Microsoft.Data.SqlClient informa de fallos de servidores y proveedores a través de SqlException. Una operación de base de datos puede devolver múltiples errores, así que inspecciona la colección Errors de la excepción en lugar de confiar solo en el Message combinado.
Las advertencias y mensajes informativos del motor de base de datos se entregan a través de InfoMessage. Suscríbete antes de abrir la conexión o ejecutar el comando que puede producir el mensaje.
Gestiona un fallo de base de datos en este orden:
- Intercepte
SqlExceptionen un punto límite que pueda añadir contexto de operación o seleccionar una acción de recuperación. - Revisa cada
SqlErrorde la colecciónErrors. - Graba campos diagnósticos estructurados sin registrar credenciales ni valores sensibles.
- Comprueba el estado de la conexión tras un fallo relacionado con la conexión.
- Inténtalo solo cuando el error sea transitorio y la operación sea segura de repetir.
Los ejemplos de este artículo asumen que un connection abierto llamado SqlConnection apunta a la base de datos de ejemplo de AdventureWorks. Los ejemplos de C# requieren using Microsoft.Data.SqlClient;.
Recibir mensajes informativos
El InfoMessage evento recibe mensajes de servidor de baja severidad, incluyendo la salida de PRINT. Un evento puede contener varios SqlError objetos. Suscríbete antes de ejecutar el comando que puede producir un mensaje:
connection.InfoMessage += (_, eventArgs) =>
{
foreach (SqlError message in eventArgs.Errors)
{
Console.WriteLine(
$"Info {message.Number}, class {message.Class}: {message.Message}");
}
};
using var command = new SqlCommand(
"PRINT N'Connected and ready.';",
connection);
command.ExecuteNonQuery();
Se imprime el fragmento:
Info 0, class 0: Connected and ready.
Catch SqlException
Captura SqlException en torno a la operación de base de datos que puedas manejar o enriquecer. No utilices una captura general para convertir una operación de base de datos fallida en un resultado exitoso.
Una operación de base de datos puede devolver más de un error. Cada SqlError de Errors incluye campos estructurados:
| Property | Meaning |
|---|---|
Number |
Número de error del motor de base de datos o del proveedor. |
Class |
Gravedad. |
State |
Estado definido por el servidor que puede distinguir ubicaciones para el mismo número de error. |
LineNumber |
Número de línea reportado. |
Procedure |
Procedimiento o nombre del disparador cuando se proporciona. |
Server |
Nombre del servidor indicado para el error. |
Message |
Texto de error legible por humanos. |
Las propiedades de conveniencia sobre SqlException, incluyendo Number, Class, State, LineNumber, Procedure, , y Server, reflejan solo el primer error. El siguiente extracto imprime un mensaje y luego genera un error en el mismo batch. Lee exception.Number y luego itera Errors para informar cada entrada:
using (var failing = new SqlCommand("""
PRINT N'Starting controlled operation.';
RAISERROR (N'The controlled operation failed.', 16, 1);
""", connection))
{
try
{
await failing.ExecuteNonQueryAsync();
}
catch (SqlException exception)
{
Console.WriteLine($"First error number: {exception.Number}");
foreach (SqlError error in exception.Errors)
{
Console.WriteLine(
$"Number={error.Number}; Class={error.Class}; " +
$"State={error.State}; Message={error.Message}");
}
}
}
Se imprime el fragmento:
First error number: 50000
Number=50000; Class=16; State=1; Message=The controlled operation failed.
Number=0; Class=0; State=1; Message=Starting controlled operation.
La salida PRINT no genera InfoMessage. Cuando un lote devuelve un error que genera SqlException, el proveedor añade los mensajes informativos de ese lote al final de SqlException.Errors.
Este extracto detecta el error controlado solo para mostrar sus campos. En el código de aplicación, relanza con throw; a menos que el bloque de captura elija una acción real de recuperación. Toma decisiones a partir de números de error documentados y del contexto de la operación. No analices textos localizados de mensajes.
Compruebe el estado de conexión.
El motor de la base de datos asigna una clase de severidad a cada mensaje. Las clases inferiores son solo informativas. Las clases tratadas como errores producen SqlException. Errores de alta gravedad pueden dejar la conexión cerrada o rota.
Después de un error, comprueba State antes de reutilizar la conexión:
using var command = new SqlCommand(
"RAISERROR (N'Controlled failure.', 16, 1);",
connection);
try
{
command.ExecuteNonQuery();
}
catch (SqlException)
{
Console.WriteLine($"Connection state: {connection.State}");
}
No des por hecho que una conexión sigue siendo utilizable porque la excepción fue capturada. Un error de gravedad 16 normalmente lo deja abierto, pero fallos que cortan la conexión pueden cerrarlo. Desecha una conexión cerrada o rota y consigue otra conexión del grupo de conexiones. No codifiques la política de reintentos solo por la gravedad. La operación también debe ser segura para repetirla.
Identificar un comando por lotes con errores
Cuando SqlBatch falla, SqlException.BatchCommand puede referenciar el SqlBatchCommand asociado. La propiedad es null cuando el proveedor no puede atribuir el fallo a un solo comando por lote. El siguiente extracto ejecuta dos comandos por lote. El segundo falla:
using (var batch = new SqlBatch(connection))
{
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1;"));
batch.BatchCommands.Add(new SqlBatchCommand("SELECT 1 / 0;"));
try
{
await batch.ExecuteNonQueryAsync();
}
catch (SqlException exception)
{
Console.WriteLine($"Error {exception.Number}: {exception.Message}");
if (exception.BatchCommand is SqlBatchCommand failedCommand)
{
Console.WriteLine($"Failed command: {failedCommand.CommandText}");
}
}
}
El fragmento se imprime:
Error 8134: Divide by zero error encountered.
Failed command: SELECT 1 / 0;
El texto de comandos puede contener literales sensibles en aplicaciones mal parametrizadas. Prefiera nombres estables de operaciones de aplicación en los registros de producción y no registre los valores de los parámetros por defecto.
Enrutar errores de usuario a InfoMessage
FireInfoMessageEventOnUserErrors se establece de forma predeterminada en false. Cuando lo configuras en true, los errores de gravedad 16 o inferior se envían al controlador InfoMessage en lugar de generar SqlException:
connection.InfoMessage += (_, eventArgs) =>
{
foreach (SqlError message in eventArgs.Errors)
{
Console.WriteLine(
$"Info {message.Number}, class {message.Class}: {message.Message}");
}
};
connection.FireInfoMessageEventOnUserErrors = true;
using var command = new SqlCommand(
"RAISERROR (N'Routed to InfoMessage.', 16, 1);",
connection);
command.ExecuteNonQuery();
No se inicia ninguna excepción. Se imprime el fragmento:
Info 50000, class 16: Routed to InfoMessage.
Utilice esta opción solo cuando la aplicación tenga un modelo de error deliberadamente basado en eventos, porque el código que llama ya no ve una excepción. Los errores con gravedad 17 o superior siguen generando SqlException. Establece la propiedad una vez como parte de la configuración de la conexión en lugar de alternarla para cada comando individual.
Contexto de diagnóstico del registro
Captura suficiente contexto estructurado para correlacionar el fallo sin exponer los datos. El siguiente extracto registra el nombre de la operación de la aplicación, el ClientConnectionId, y los campos de cada SqlError.
ClientConnectionId ayuda a correlacionar un fallo del cliente con el diagnóstico de servidores y red. No es una credencial.
const string operation = "LoadOrders";
using (var load = new SqlCommand("SELECT 1 / 0;", connection))
{
try
{
await load.ExecuteScalarAsync();
}
catch (SqlException exception)
{
Console.WriteLine(
$"Operation={operation}; " +
$"ClientConnectionId={exception.ClientConnectionId}");
foreach (SqlError error in exception.Errors)
{
Console.WriteLine(
$"Number={error.Number}; Class={error.Class}; " +
$"State={error.State}; Procedure={error.Procedure}; " +
$"Line={error.LineNumber}");
}
}
}
El ClientConnectionId valor varía en cada conexión. El extracto imprime resultados similares a:
Operation=LoadOrders; ClientConnectionId=<connection ID>
Number=8134; Class=16; State=1; Procedure=; Line=1
También anota si una transacción estaba activa y, si BatchCommand está disponible, qué comando batch falló. Incluye identificadores de servidores y bases de datos sanitizados solo si la política organizativa lo permite. No registres cadenas de conexión, tokens de acceso, contraseñas ni valores sensibles de parámetros. Utiliza contadores de diagnóstico, rastreo de fuentes de eventos y la guía de solución de problemas de SqlClient para una investigación más profunda.
Contenido relacionado
- Vuelve al flujo de trabajo de ejecución de comandos.
- Para identificar un comando fallido en un grupo, ejecuta varios comandos con SqlBatch.
- Para añadir intentos acotados, utiliza lógica de reintentos configurable.
- Cuando los campos de excepción no son suficientes, usa la guía de solución de problemas de SqlClient.