Sei sulla pagina 1di 6

Comprender el objeto CommandBuilder

El objeto CommandBuilder funciona de una manera bastante extraña. No modifica el


objeto DataAdapter, pero se registra a sí mismo como un detector para RowUpdating
del DataAdapter evento (que discutiré más adelante en este capítulo), para que pueda
construir el comando correcto para ser enviado a la base de datos para cada fila en el
DataTable. Puede usar los métodos GetxxxxCommand de CommandBuilder para
asignar explícitamente las propiedades InsertCommand, DeleteCommand y
UpdateCommand del objeto DataAdapter:

Dim cmdBuilder As New OleDbCommandBuilder(da)

da.InsertCommand = cmdBuilder.GetInsertCommand()

da.DeleteCommand = cmdBuilder.GetDeleteCommand()

da.UpdateCommand = cmdBuilder.GetUpdateCommand()

Para entender los beneficios y desventajas de usar el objeto CommandBuilder,


necesita echar un vistazo más de cerca a estos comandos SQL. Comencemos con el
INSERTO comando que el objeto OleDbCommandBuilder creado para administrar
inserciones en el Tabla de editores:

INSERT INTO [Publishers]( [pub_id] , [pub_name] , [city] , [state] , [country] )

VALUES (?,?,?,?,?)
Como puede ver, este comando es directo. Simplemente inserte un nuevo registro y complete su
campos con los argumentos que se pasarán al objeto InsertCommand. El objeto CommandBuilder ha
creado e inicializado convenientemente la colección Parameters,
con cada parámetro apuntando al valor real que tiene la columna correspondiente
en la DataTable

El comando DELETE también es relativamente simple, pero su cláusula WHERE tiene que tener en
cuenta por el hecho de que algunos campos (otros que la clave primaria pub_id) pueden ser nulos cuando
el registro se lee de la base de datos. Entonces no puedes simplemente usar el operador de igualdad,
que por defecto el lenguaje T-SQL evalúa a nulo en lugar de verdadero cuando ambos
los operandos son nulos:

DELETE FROM [Publishers] WHERE ( ([pub_id] = ?)


AND ((? = 1 AND [pub_name] IS NULL) OR ([pub_name] = ?))
AND ((? = 1 AND [city] IS NULL) OR ([city] = ?))
AND ((? = 1 AND [state] IS NULL) OR ([state] = ?))
AND ((? = 1 AND [country] IS NULL) OR ([country] = ?)) )

La necesidad de tener en cuenta los valores nulos hace que la sintaxis del comando SQL sea excesiva
Complejo.
El significado de la cláusula WHERE se puede resumir de la siguiente manera: Eliminar la fila
cuyo campo pub_id (la clave principal) es igual al valor proporcionado por el primer parámetro, siempre
que el valor actual en la base de datos para todas las demás columnas sea el mismo
como lo fue cuando el DataTable se llenó con datos de la base de datos. Curiosamente, CommandBuilder
genera este tipo de código para todos los campos de la base de datos, incluidos los que
no son nulos. Cuando un campo no admite nulos, esta precaución es innecesaria, pero no
tener un impacto notable en el rendimiento tampoco.

El comando UPDATE es el más complicado del lote porque su cláusula WHERE


utiliza los valores actuales para todos los campos en la cláusula SET y los valores originales en
La cláusula WHERE para ubicar el registro que debe actualizarse:

UPDATE [Publishers]SET [pub_id]=?,[pub_name]=?,


[city] = ? , [state] = ? , [country] = ?
WHERE ( ([pub_id] = ?)
AND ((? = 1 AND [pub_name] IS NULL) OR ([pub_name] = ?))
AND ((? = 1 AND [city] IS NULL) OR ([city] = ?))
AND ((? = 1 AND [state] IS NULL) OR ([state] = ?))
AND ((? = 1 AND [country] IS NULL) OR ([country] = ?)) )

Los comandos SQL que produce el objeto CommandBuilder dependen de .NET


proveedor de datos que estás usando. Usted ve la diferencia cuando accede a la misma tabla de la base de
datos utilizando el proveedor de datos .NET de SQL Server nativo, que debe usar el parámetro
nombres con el prefijo @, por ejemplo:

INSERT INTO Publishers ( pub_id, pub_name, city, state, country )


VALUES ( @p1 , @p2, @p3, @p4, @p5 )

Debe comprender la diferencia entre utilizar valores de columna originales y valores de


columna actuales. Puede determinar qué versión del valor de columna utiliza explorando la
colección Parámetros y comprobando las propiedades SourceColumn y SourceVersion de cada
parámetro. Esta es la rutina genérica que utilizo para mis exploraciones y que funciona con
cualquier proveedor de datos .NET:

Sub DumpCommand(ByVal cmd As IDbCommand)


Debug.WriteLine(cmd.CommandText)
For Each p As IDbDataParameter In cmd.Parameters
Debug.WriteLine(String.Format(“ {0} {1} ({2} {3})", _
p.ParameterName, p.DbType, p.SourceVersion, p.SourceColumn))
Next
End Sub

Este es el resultado en la ventana de depuración que produce la rutina de comando de volcado


cuando se aplica al comando ACTUALIZAR producido por el objeto SqlCommandBuilder:

UPDATE Publishers SET pub_id = @p1,pub_name=@p2 ,city=@p3,

state = @p4 , country = @p5 WHERE ( (pub_id = @p6)

AND ((@p7 = 1 AND pub_name IS NULL) OR (pub_name = @p8))

AND ((@p9 = 1 AND city IS NULL) OR (city = @p10))

AND ((@p11 = 1 AND state IS NULL) OR (state = @p12))

AND ((@p13 = 1 AND country IS NULL) OR (country = @p14)) )

@p1 AnsiStringFixedLength (Current pub_id)

@p2 AnsiString (Current pub_name)

@p3 AnsiString (Current city)


@p4 AnsiStringFixedLength (Current state)

@p5 AnsiString (Current country)

@p6 AnsiStringFixedLength (Original pub_id)

@p7 Int32 (Current )

@p8 AnsiString (Original pub_name)

@p9 Int32 (Current )

@p10 AnsiString (Original city)

@p11 Int32 (Current )

@p12 AnsiStringFixedLength (Original state)

@p13 Int32 (Current )

@p14 AnsiString (Original country)

Aquí hay algunos detalles más sobre el objeto CommandBuilder y algunas de sus limitaciones:

■ El comando SELECT original asignado al DataAdapter (que también es


expuesto por la propiedad SelectCommand) puede hacer referencia solo a una tabla.

■ La tabla de origen debe incluir una clave principal o al menos una columna con un único
restricción, y el resultado devuelto por la instrucción SELECT debe incluir esa columnas. Las claves
primarias que consisten en columnas múltiples son compatibles.

■ El objeto InsertCommand inserta solo las columnas que son actualizables y omite
correctamente la identidad, la marca de tiempo y las columnas calculadas y, en general, todas
las columnas generadas por el motor de la base de datos.

■ El objeto UpdateCommand utiliza los valores de todas las columnas originales en las
cláusulas WHERE, incluida la clave principal, pero omite correctamente la marca de tiempo y
las columnas calculadas de la cláusula SET.

■ El objeto DeleteCommand usa los valores de todas las columnas originales en el Cláusula
WHERE para ubicar la fila que debe eliminarse.

■ SqlCommandBuilder y OdbcCommandBuilder generan comandos no válidos cuando el


nombre de una tabla o columna contiene un espacio o un carácter especial. (Esto no es un
problema con el objeto OleDbCommandBuilder).

Puede resolver fácilmente el último problema forzando al SqlCommandBuilder o al


OdbcCommandBuilder a usar un prefijo y un sufijo para todos los nombres de tabla y
columna utilizados en el comando generado Puede hacer esto asignando una cadena
al QuotePrefix y Propiedades de QuoteSuffix para que el texto de SQL resultante
cumpla con la sintaxis esperada por la base de datos de destino:
cmdBuilder.QuotePrefix = “["

cmdBuilder.QuoteSuffix = “]”
Tenga en cuenta que CommandBuilder ejecuta el comando SQL asignado al
La propiedad CommandText de SelectCommand para generar los otros tres comandos, por lo que si
luego cambias el comando SELECCIONAR, los comandos de lectura y actualización estarán fuera de
sincronizar Por esta razón, siempre debe invocar RefreshSchema de CommandBuilder
método siempre que modifique la propiedad SelectCommand.

Personalización de los comandos Insertar, Actualizar y Eliminar.

Después de que comprenda cómo funcionan las propiedades InsertCommand, UpdateCommand y


DeleteCommand, es relativamente fácil crear sus comandos personalizados. Esta técnica requiere que
escriba más código del que tiene que cuando usa el
Objeto CommandBuilder, pero le permite generar código más rápido y escalable, ambos
porque evitas un viaje redondo al servidor y porque un comando bien escrito
puede reducir el número de conflictos de actualización. En general, el objeto InsertCommand producido
por CommandBuilder está bien para la mayoría de los propósitos. En la siguiente discusión, me centraré
solo en los objetos UpdateCommand y DeleteCommand.

El comando DELETE generado por CommandBuilder utiliza los valores originales de


todas las columnas en su cláusula WHERE para ubicar el registro que debe
eliminarse. Este enfoque implementa la llamada estrategia de bloqueo optimista, que
es la más segura porque asegura que no se borre ningún registro si otro usuario ha
cambiado una o más columnas mientras tanto.

Sin embargo, en algunas aplicaciones, puede ser necesario adoptar una estrategia
diferente para las eliminaciones y decidir eliminar el registro incluso si otro usuario ha
modificado cualquiera de sus campos (que no sean la clave principal). De acuerdo con
esta estrategia, a veces apodada como la estrategia "el último gana uno", la operación
más elegida siempre gana y tiene éxito actualiza el registro (a menos que otro usuario
haya eliminado el registro o cambiado su campo de clave principal). Puede aplicar esta
estrategia simplemente usando solo el campo de clave principal en la cláusula
WHERE. Esta técnica es más rápida y más escalable, pero debe asegurarse que no
invalida la lógica comercial de su aplicación.
Por ejemplo, puede decidir que es legal que un usuario elimine el registro de un autor en
la base de datos Biblio, aunque otro usuario ha modificado los mismos datos del autor en el
mientras tanto. Puede aplicar esta estrategia mediante la fabricación del DeleteCommand
usted mismo:

'Crea un comando de eliminación que filtra los registros solo por su campo Au_ID.
Dim cmdDelete As New OleDbCommand(“DELETE FROM Authors WHERE Au_ID = ?", cn)
'Crea un parámetro y establece sus propiedades.
With cmdDelete.Parameters.Add(“@p1", OleDbType.Integer)

’ This is the name of the column in the DataTable.


.SourceColumn = “Au_ID"

’ We want to use the original value in each DataRow.

.SourceVersion = DataRowVersion.Original

End With

‘ Assign command to the DeleteCommand property of the DataAdapter.

da.DeleteCommand = cmdDelete

También puede aplicar una estrategia similar para el objeto UpdateCommand al


decidir que los cambios por parte del usuario actual siempre sobrescriben los cambios
de otros usuarios que tienen modificó el mismo registro después de que el usuario
actual importó el registro en el DataSet. El siguiente código usa una sintaxis más
compacta para el método Parameters.Add y se basa en el hecho de que el valor
predeterminado para la propiedad SourceVersion es DataRowVersion.Current:

‘ Create a custom update command.

Dim cmdUpdate As New OleDbCommand( _

“UPDATE Authors SET Author = ?, [Year Born] = ? WHERE Au_ID = ?", cn)

‘ Add arguments for the SET clause. (They use current field values)

cmdUpdate.Parameters.Add(“@p1", OleDbType.VarChar, 50, “Author”)

cmdUpdate.Parameters.Add(“@p2", OleDbType.Integer, 4, “Year Born”)

’ Add the argument in the WHERE clause. (It uses the original field value.)

cmdUpdate.Parameters.Add(“@p3", OleDbType.Integer, 4, “Au_ID”).SourceVersion _

= DataRowVersion.Original

’ Assign the command to the DataAdapter’s UpdateCommand property.

da.UpdateCommand = cmdUpdate

El conocimiento de cómo funciona su aplicación a menudo le permite simplificar la estructura


de el comando ACTUALIZAR. Por ejemplo, puede tener una aplicación que muestre todo
las columnas en los registros, pero impide que los usuarios modifiquen algunas columnas (por
lo general, la clave principal u otras claves que podrían funcionar como claves externas en
otras tablas). Como resultado, puede omitir dichos campos en la cláusula SET del comando
UPDATE. Si la tabla de la base de datos contiene un campo de marca de tiempo, tiene la
oportunidad de mejorar el rendimiento de las operaciones de eliminación y actualización de una
manera segura porque en este caso, puede detectar si otro usuario ha modificado el registro en
cuestión sin verificar que todas las columnas aún contengan sus valores originales. De hecho,
un campo de marca de tiempo es se garantiza que cambia cada vez que se cambia un registro
de la base de datos, para que pueda reducir el Cláusula WHERE para incluir solo la clave
principal (que sirve para ubicar el registro) y el campo de marca de tiempo (que sirve para
garantizar que ningún usuario haya modificado el registro después de fue importado al
DataSet). Tosee cómo funciona esta técnica en la práctica, extiende la tabla Authors en la base
de datos Pubs con un campo timestamp llamado LastUpdate, y luego ejecuta este código:
Dim cmdDelete As New OleDbCommand(“DELETE FROM Authors WHERE au_id = ? And
LastUpdate=

?", cn)

cmdDelete.Parameters.Add(“@p1", OleDbType.VarWChar, 11, “au_id”)

‘ Timestamp are of VarBinary type (they map to a Byte array).

cmdDelete.Parameters.Add(“@p2", OleDbType.VarBinary, 8, “LastUpdate”)

da.DeleteCommand = cmdDelete

Dim cmdUpdate As New OleDbCommand(“UPDATE Authors “ _

& “ SET au_fname = ? , au_lname = ? WHERE au_id = ? AND LastUpdate = ?", cn)

‘ Add arguments for the SET clause, that use the current field value.

cmdUpdate.Parameters.Add(“@p1", OleDbType.VarChar, 20, “au_fname”)

cmdUpdate.Parameters.Add(“@p2", OleDbType.VarChar, 40, “au_lname”)

‘ Add the arguments in the WHERE clause that use the original field value.

cmdUpdate.Parameters.Add(“@p3", OleDbType.VarChar, 11, “au_id”).SourceVersion _

= DataRowVersion.Original

cmdUpdate.Parameters.Add(“@p4", OleDbType.VarBinary, 8, “LastUpdate”).SourceVersion _

= DataRowVersion.Original

da.UpdateCommand = cmdUpdate

Otra razón más para personalizar las propiedades InsertCommand, UpdateCommand


y DeleteCommand es aprovechar cualquier procedimiento almacenado en la base de
datos spe diseñado específicamente para insertar, modificar y eliminar registros. Al
delegar esta edición operaciones a un procedimiento almacenado y la prevención de
usuarios y aplicaciones directamente Al acceder a las tablas de la base de datos,
puede exigir un mayor control sobre la coherencia de los datos. Insertar, actualizar y
eliminar registros a través de un procedimiento almacenado no es conceptualmente
diferente de lo que he descrito hasta ahora. Consulte el apartado "Procedimientos
almacenados". en el Capítulo 21 para revisar cómo crear parámetros para los objetos
Command que llaman procedimientos almacenados.

Potrebbero piacerti anche