Ejecutar un comando

Utilice SqlCommand para ejecutar instrucciones Transact-SQL (T-SQL) o procedimientos almacenados. Un comando necesita un SqlConnection abierto. Desecha el objeto Command después de usarlo y desecha cualquier objeto Reader antes de reutilizar la conexión.

Los ejemplos de este artículo dan por hecho que un SqlConnection abierto denominado connection apunta a la base de datos de ejemplo AdventureWorks.

Los ejemplos de C# requieren using Microsoft.Data.SqlClient;. Ejemplos que usan SqlDbType, CommandType o CommandBehavior también requieren using System.Data;.

Para establecer conexiones, autenticación y cifrado, consulte Cadenas de conexión y Cifrado y validación de certificados.

Elige un método de ejecución

Elige el método que coincida con el resultado esperado:

Método Se utiliza cuando Resultado
ExecuteNonQuery El comando cambia datos o esquema. Número de filas afectadas. -1 puede ocurrir cuando no hay recuento disponible, incluso cuando SET NOCOUNT ON suprime conteos.
ExecuteScalar Necesitas un valor. La primera columna de la primera fila, o null cuando el resultado no tiene filas. Una base de datos NULL se devuelve como Value.
ExecuteReader Necesitas filas, varios conjuntos de resultados o esquema. Un objeto SqlDataReader.
ExecuteXmlReader El comando devuelve XML en la forma requerida por ExecuteXmlReader. Un valor de tipo XmlReader.

Cada método tiene un equivalente asincrónico basado en tareas. Pase un CancellationToken cuando el llamante necesite dejar de esperar. Para el comportamiento de cancelación y tiempos de espera, véase Programación asíncrona.

Ejecutar un comando de texto

CommandType.Text es el valor predeterminado. Coloca la sentencia en CommandText y pasa los valores a través de los parámetros:

const string sql = """
    SELECT ProductID, Name, ListPrice
    FROM Production.Product
    WHERE ProductSubcategoryID = @subcategoryId
    ORDER BY ListPrice DESC;
    """;

using var command = new SqlCommand(sql, connection);
command.Parameters.Add("@subcategoryId", SqlDbType.Int).Value = 2;

using SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
    Console.WriteLine(
        $"{reader.GetInt32(0)} | {reader.GetString(1)} | {reader.GetDecimal(2):C}");
}

Para tipos de parámetros, tamaños, valores nulos y direcciones, consulte Configurar parámetros. No concatenes valores no confiables en CommandText.

Ejecución de un procedimiento almacenado

Establece CommandType.StoredProcedure y utiliza el nombre del procedimiento cualificado por el esquema:

using var command = new SqlCommand(
    "dbo.uspGetEmployeeManagers",
    connection);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(
    "@BusinessEntityID",
    SqlDbType.Int).Value = 6;

using SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
    Console.WriteLine(
        $"{reader.GetString(2)} {reader.GetString(3)}");
}

Microsoft. Data.SqlClient soporta CommandType.Text y CommandType.StoredProcedure. Al establecer CommandType.TableDirect se produce una excepción ArgumentOutOfRangeException.

Comprueba qué devuelve cada método

Usa ExecuteScalar cuando necesites un valor:

using var command = new SqlCommand(
    "SELECT COUNT(*) FROM Production.Product WHERE SellEndDate IS NULL;",
    connection);

int currentProductCount = (int)command.ExecuteScalar()!;
Console.WriteLine($"Current products: {currentProductCount}");

ExecuteScalar devuelve null cuando el resultado no tiene filas y DBNull.Value cuando el primer valor es una NULL de la base de datos.

Utilice ExecuteNonQuery para instrucciones que modifican datos. Este ejemplo revierte su cambio para que AdventureWorks permanezca sin cambios:

using SqlTransaction transaction = connection.BeginTransaction();

using var command = new SqlCommand("""
    UPDATE Sales.SalesOrderHeader
    SET Status = @status
    WHERE SalesOrderID = @salesOrderId;
    """, connection, transaction);
command.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
command.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43659;

int rowsAffected = command.ExecuteNonQuery();
Console.WriteLine($"Rows affected: {rowsAffected}");

transaction.Rollback();

SET NOCOUNT ON suprime los conteos de filas afectadas, por lo que ExecuteNonQuery puede devolver -1 incluso cuando una instrucción cambia datos.

Usa ExecuteXmlReader para un FOR XML resultado:

using var command = new SqlCommand("""
    SELECT TOP (3) ProductID, Name
    FROM Production.Product
    ORDER BY ProductID
    FOR XML PATH('Product'), ROOT('Products');
    """, connection);

using System.Xml.XmlReader reader = command.ExecuteXmlReader();
reader.MoveToContent();
Console.WriteLine(reader.ReadOuterXml());

Utiliza una transacción local

Cuando una conexión tiene un SqlTransaction local activo, asigne esa transacción a cada comando ejecutado en la conexión. Este ejemplo revierte su cambio para que AdventureWorks permanezca sin cambios:

using SqlTransaction transaction = connection.BeginTransaction();

using var command = new SqlCommand("""
    UPDATE Sales.SalesOrderHeader
    SET Status = @status
    WHERE SalesOrderID = @salesOrderId;
    """, connection, transaction);
command.Parameters.Add("@status", SqlDbType.TinyInt).Value = 2;
command.Parameters.Add("@salesOrderId", SqlDbType.Int).Value = 43659;

command.ExecuteNonQuery();
transaction.Rollback();

Llama Commit en vez de Rollback solo cuando la aplicación acepta la transacción completa. Un comando que no hace referencia a la transacción activa lanza InvalidOperationException. La ejecución también falla si la transacción pertenece a otra conexión. Para los límites de transacción y el rollback, véase Transacciones locales.

Establecer un tiempo de espera de comando

CommandTimeout limita la ejecución de comandos y las esperas acumuladas de lectura de red en segundos. Un comando utiliza la configuración de Command Timeout de la conexión cuando hay alguna configurada. Si no, el valor por defecto son 30 segundos. Un valor de 0 no tiene límite de tiempo. Los valores negativos lanzan una excepción ArgumentException.

Cuando expira el tiempo de espera, el comando lanza SqlException con Number fijado en -2:

using var command = new SqlCommand(
    "WAITFOR DELAY '00:00:05';",
    connection)
{
    CommandTimeout = 1
};

try
{
    command.ExecuteNonQuery();
}
catch (SqlException exception) when (exception.Number == -2)
{
    Console.WriteLine("The command timed out.");
}

Connect Timeout limita el establecimiento de conexiones y las esperas del grupo de conexiones. CommandTimeout se inicia después de que comienza la ejecución del comando. Para más información, consulte Opciones de conexión.

Usa un tiempo límite de espera y un token de cancelación juntos. El tiempo de espera establece un límite en la operación de la base de datos. Cancelar el token puede devolver el control al llamante antes de que expire el tiempo de espera. No des por hecho que la cancelación y el tiempo de espera producen la misma excepción.

Reutilizar y preparar comandos

Añade un parámetro una vez y luego cambia su valor una vez finalizada cada ejecución:

using var command = new SqlCommand("""
    SELECT TOP (1) Name, ListPrice
    FROM Production.Product
    WHERE ProductSubcategoryID = @subcategoryId
    ORDER BY ListPrice DESC;
    """, connection);
SqlParameter subcategoryId = command.Parameters.Add(
    "@subcategoryId",
    SqlDbType.Int);

foreach (int id in new[] { 1, 2, 3 })
{
    subcategoryId.Value = id;
    using SqlDataReader reader = command.ExecuteReader();
    if (reader.Read())
    {
        Console.WriteLine($"{id}: {reader.GetString(0)}");
    }
}

Llama Prepare solo cuando las medidas lo justifiquen. Prepare No prepara procedimientos almacenados ni comandos de texto sin parámetros. Para texto parametrizado, establece metadatos completos como Size, Scale y Prepare en cada parámetro antes de llamar a Precision.

Comandos para solucionar problemas

Captura SqlException en torno a la operación de la base de datos e inspecciona su Errors colección cuando el servidor devuelva más de un error. Para errores de comando, advertencias e mensajes informativos, véase Manejar errores y mensajes.

Utiliza contadores de diagnóstico, rastreo de fuentes de eventos y la guía de solución de problemas de SqlClient cuando un error requiera más contexto. Aplica lógica de reintento configurable solo cuando repetir el comando sea seguro.