Compartir a través de


Comandos generados automáticamente

En los casos en que SelectCommand se especifica de forma dinámica en tiempo de ejecución, por ejemplo a través de una herramienta de consulta que acepta un comando de texto del usuario, existe la posibilidad de que no se pueda especificar adecuadamente en tiempo de diseño si se trata de un comando InsertCommand, UpdateCommand o DeleteCommand. Si DataTable está asociado a una tabla de una base de datos o se ha generado a partir de ella, puede usar el objeto CommandBuilder para generar automáticamente los comandos DeleteCommand, InsertCommand y UpdateCommand de DataAdapter.

El requisito mínimo para que la generación automática de comandos funcione correctamente consiste en establecer la propiedad SelectCommand. El esquema de tabla que recupera SelectCommand determina la sintaxis de las instrucciones INSERT, UPDATE y DELETE generadas automáticamente.

CommandBuilder debe ejecutar SelectCommand con el objeto de poder devolver los metadatos necesarios para construir los comandos de inserción, actualización y eliminación. Por eso es necesario realizar un viaje adicional al origen de datos, con el consiguiente efecto adverso en el rendimiento. Para mejorar el rendimiento, debe especificar los comandos de forma explícita, en lugar de utilizar CommandBuilder.

SelectCommand debe además devolver al menos una clave principal o columna única. Si no hay ninguna, se inicia una excepción InvalidOperation y no se genera ningún comando.

Cuando se asocia con un DataAdapter, CommandBuilder genera automáticamente las propiedades InsertCommand, UpdateCommand y DeleteCommand del DataAdapter, si son referencias nulas. Si ya existe algún comando Command para una propiedad, se usará ese.

Las vistas de bases de datos creadas al unir una o varias tablas no se consideran una tabla única de base de datos. En este caso no puede usar CommandBuilder para generar automáticamente comandos, por lo que será necesario especificarlos de forma explícita. Para obtener más información sobre cómo especificar comandos de forma explícita para reflejar en el origen de datos las actualizaciones efectuadas en el DataSet, vea Actualizar la base de datos con un DataAdapter y el DataSet.

Puede ser aconsejable asignar los parámetros de salida a la fila actualizada de un DataSet. Una tarea habitual consiste en recuperar a partir del origen de datos el valor de un campo de identidad de generación automática o una marca de hora. CommandBuilder no asigna de forma predeterminada los parámetros de salida a las columnas de una fila actualizada. En ese caso debe especificar de forma explícita el comando. Para consultar un ejemplo de asignación de un campo de identidad de generación automática a una columna de una fila insertada, vea Recuperar valores de identidad o de autonumeración.

Reglas para comandos generados automáticamente

En la tabla siguiente se muestran las reglas de la generación automática de comandos.

Comando Regla
InsertCommand Inserta una fila en el origen de datos para cada fila de la tabla cuyo estado RowState sea DataRowState.Added. Inserta valores en todas las columnas que se pueden actualizar (excepto en columnas como identidades, expresiones y marcas de hora).
UpdateCommand Actualiza las filas del origen de datos que en la tabla tienen DataRowState.Modified como estado RowState. Actualiza los valores de todas las columnas excepto las que no se pueden actualizar, como identidades o expresiones. Actualiza todas las filas en las que los valores de columna en el origen de datos coinciden con los valores de la columna de clave principal de la fila, siempre que las restantes columnas del origen de datos coincidan con los valores originales de la fila. Para obtener más información, vea la sección "Modelo de simultaneidad optimista para actualizaciones y eliminaciones" de este mismo tema.
DeleteCommand Elimina las filas del origen de datos que corresponden a todas las filas de la tabla con un estado RowState de DataRowState.Deleted. Elimina todas las filas en las que los valores de columna coinciden con los valores de la columna de clave principal de la fila, siempre que las restantes columnas del origen de datos coincidan con los valores originales de la fila. Para obtener más información, vea la sección "Modelo de simultaneidad optimista para actualizaciones y eliminaciones" de este mismo tema.

Modelo de simultaneidad optimista para actualizaciones y eliminaciones

La lógica empleada en la generación automática de comandos para las instrucciones UPDATE y DELETE se basa en la simultaneidad optimista. Es decir, los registros no se bloquean para ser editados y pueden ser modificados en cualquier momento por otros usuarios o procesos. Dado que existe la posibilidad de que un registro haya sido modificado después de que haya sido devuelto por la instrucción SELECT y antes de que se emita la instrucción UPDATE o DELETE, la instrucción UPDATE o DELETE generada automáticamente incluye una cláusula WHERE tal que la fila sólo se actualiza cuando contiene todos los valores originales y no ha sido eliminada del origen de datos. Esto evita que se sobrescriban los datos nuevos. En los casos en que una actualización generada automáticamente trata de actualizar una fila que ha sido eliminada o que no contiene los valores originales que se encuentran en el DataSet, el comando no tiene ningún efecto en los registros y se inicia una excepción DBConcurrencyException.

Si desea que la instrucción UPDATE o DELETE se ejecute sin tener en cuenta los valores originales, debe establecer de forma explícita el UpdateCommand del DataAdapter sin utilizar la generación automática de comandos.

Limitaciones de la lógica de generación automática de comandos

La generación automática de comandos tiene las siguientes limitaciones.

Sólo tablas no relacionadas

La lógica de generación automática de comandos crea instrucciones INSERT, UPDATE o DELETE para tablas independientes sin tener en cuenta las relaciones que éstas puedan tener con otras tablas en el origen de datos. Por eso se puede producir un error al llamar a Update para realizar cambios en una columna que participa en una restricción de clave externa en la base de datos. Para evitar esa excepción, no use CommandBuilder al actualizar las columnas que participan en una restricción de clave externa. En este caso debe especificar de forma explícita las instrucciones que se van a utilizar para llevar a cabo la operación.

Nombres de tabla y columna

La lógica de generación automática de comandos ocasiona un error cuando los nombres de las tablas o columnas incluyen algún carácter especial, como espacios, puntos, signos de exclamación y otros caracteres no alfanuméricos, aún en el caso de que se incluyan entre corchetes. Se pueden utilizar nombres completos de tabla, como catalog.schema.table.

Utilizar CommandBuilder para generar automáticamente una instrucción SQL

Para generar automáticamente instrucciones SQL para un DataAdapter, debe establecer en primer lugar la propiedad SelectCommand del DataAdapter. A continuación, debe crear un objeto CommandBuilder y especificar en él como argumento el DataAdapter para el que va a generar automáticamente las instrucciones SQL.

Dim custDA As SqlDataAdapter = New SqlDataAdapter("SELECT * FROM Customers", nwindConn)
Dim custCB As SqlCommandBuilder = New SqlCommandBuilder(custDA)
custCB.QuotePrefix = "["
custCB.QuoteSuffix = "]"

Dim custDS As DataSet = New DataSet

nwindConn.Open()
custDA.Fill(custDS, "Customers")

' Code to modify data in the DataSet here.

' Without the SqlCommandBuilder, this line would fail.
custDA.Update(custDS, "Customers")
nwindConn.Close()
[C#]
SqlDataAdapter custDA = new SqlDataAdapter("SELECT * FROM Customers", nwindConn);
SqlCommandBuilder custCB = new SqlCommandBuilder(custDA);
custCB.QuotePrefix = "[";
custCB.QuoteSuffix = "]";

DataSet custDS = new DataSet();

nwindConn.Open();
custDA.Fill(custDS, "Customers");

// Code to modify data in the DataSet here.

// Without the SqlCommandBuilder, this line would fail.
custDA.Update(custDS, "Customers");
nwindConn.Close();

Modificar SelectCommand

Se puede iniciar una excepción si modifica el texto CommandText de SelectCommand después de generar automáticamente comandos de inserción, actualización o eliminación. Si el texto de SelectCommand.CommandText modificado contiene información del esquema que sea incoherente con la del texto de SelectCommand.CommandText utilizado en el momento de la generación automática de los comandos de inserción, actualización o eliminación, las futuras llamadas al método DataAdapter.Update pueden tratar de tener acceso a columnas que ya no existan en la tabla actual a la que hace referencia SelectCommand, con lo que se inicia una excepción.

Puede actualizar la información del esquema que utiliza CommandBuilder para generar automáticamente los comandos; para ello, basta con llamar al método RefreshSchema de CommandBuilder.

Si desea conocer el comando generado automáticamente, puede obtener una referencia a los comandos generados automáticamente mediante los métodos GetInsertCommand, GetUpdateCommand y GetDeleteCommand del objeto CommandBuilder. Después, examine la propiedad CommandText del comando asociado.

En el ejemplo de código siguiente se escribe en la consola el comando de actualización generado automáticamente.

Console.WriteLine(custCB.GetUpdateCommand().CommandText)

En el ejemplo siguiente se amplía el código del ejemplo anterior (en la sección "Utilizar CommandBuilder para generar automáticamente una instrucción SQL") y se reproduce la tabla Customers, sustituyendo la columna CompanyName por la columna ContactName. Se llama al método RefreshSchema para actualizar los comandos generados automáticamente con la información de esta nueva columna.

nwindConn.Open()

custDA.SelectCommand.CommandText = "SELECT CustomerID, ContactName FROM Customers"
custCB.RefreshSchema()

custDS.Tables.Remove(custDS.Tables("Customers"))
custDA.Fill(custDS, "Customers")

' Code to modify the new table in the DataSet here.

' Without the call to RefreshSchema, this line would fail.
custDA.Update(custDS, "Customers")

nwindConn.Close()
[C#]
nwindConn.Open();

custDA.SelectCommand.CommandText = "SELECT CustomerID, ContactName FROM Customers";
custCB.RefreshSchema();

custDS.Tables.Remove(custDS.Tables["Customers"]);
custDA.Fill(custDS, "Customers");

// Code to modify the new table in the DataSet here.

// Without the call to RefreshSchema, this line would fail.
custDA.Update(custDS, "Customers");

nwindConn.Close();

Vea también

Utilizar proveedores de datos de .NET Framework para obtener acceso a datos | OleDbDataAdapter (Clase) | OdbcDataAdapter (Clase) | SqlDataAdapter (Clase)