Compartir a través de


Actualizar la base de datos con un DataAdapter y el DataSet

El método Update de DataAdapter se llama para reflejar en el origen de datos los cambios efectuados en un DataSet. El método Update, al igual que el método Fill, acepta como argumentos una instancia de un DataSet y, de forma opcional, un objeto DataTable o un nombre de DataTable. La instancia de DataSet es el DataSet que contiene los cambios efectuados, mientras que DataTable identifica la tabla desde la que se pueden recuperar esos cambios.

Al llamar al método Update, el DataAdapter analiza los cambios efectuados y ejecuta el comando apropiado (INSERT, UPDATE o DELETE). Cuando el DataAdapter encuentra un cambio en una DataRow, utiliza los comandos InsertCommand, UpdateCommand o DeleteCommand para reflejarlo. De esta forma, se obtiene el máximo rendimiento de la aplicación de ADO.NET al especificar la sintaxis del comando en la fase de diseño y utilizar, siempre que es posible, procedimientos almacenados. Antes de llamar a Update debe establecer de forma explícita los comandos. Si se llama a Update y el comando correspondiente a una actualización determinada no existe (por ejemplo, no hay un comando DeleteCommand para las filas eliminadas), se inicia una excepción.

Se pueden usar los parámetros de Command para especificar los valores de entrada y de salida de una instrucción SQL o un procedimiento almacenado para cada fila modificada en un DataSet. Para obtener más información, vea Utilizar parámetros con DataAdapter.

Si el 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 del DataAdapter. Para obtener más información, vea Comandos generados automáticamente.

El método Update refleja en el origen de datos los cambios efectuados, sin embargo, puede que otros clientes hayan modificado datos en el origen de datos desde el momento en que se llenó a partir de él el DataSet. Para actualizar el DataSet con los datos actuales, debe usar el DataAdapter y llenar (con Fill) el DataSet de nuevo. De esta forma se agregan las filas nuevas a la tabla y se actualiza la información en las filas ya existentes. El método Fill determina si se va a agregar una nueva fila o si se va a actualizar una fila existente mediante el examen de los valores de clave principal de las filas del DataSet y las filas devueltas por SelectCommand. Si el método Fill encuentra un valor de clave principal para una fila del DataSet que coincide con un valor de clave principal de una fila de los resultados devueltos por SelectCommand, éste actualiza la fila existente con la información de la fila devuelta por SelectCommand y establece el RowState de la fila existente en Unchanged. Si una fila devuelta por SelectCommand tiene un valor de clave principal que no coincide con ninguno de los valores de clave principal de las filas del DataSet, el método Fill agrega una nueva fila con un RowState de Unchanged.

Nota   Si SelectCommand devuelve los resultados de una combinación externa (OUTER JOIN), DataAdapter no establecerá un valor PrimaryKey para la tabla DataTable resultante. El usuario tendrá que definir PrimaryKey para asegurarse de que las filas duplicadas se resuelven correctamente. Para obtener más información, vea Definir una clave principal para una tabla.

Para tratar de las excepciones que se pueden producir durante una actualización Update, puede usar el evento RowUpdated con el fin de responder a los errores de actualización de filas cuando se produzcan (vea Trabajar con eventos DataAdapter) o puede asignar el valor true a DataAdapter.ContinueUpdateOnError antes de llamar a Update, y responder a la información de error almacenada en la propiedad RowError de una fila en particular cuando termine la actualización Update (vea Agregar y leer información de error de fila).

**Nota   **Si se llama a AcceptChanges en el DataSet, DataTable o DataRow, todos los valores Original de una DataRow se sobrescribirán con los valores Current de la DataRow. Si se han modificado los valores de campo que identifica de forma única a una fila, los valores Original dejarán de coincidir con los valores del origen de datos después de llamar a AcceptChanges.

En los ejemplos siguientes se demuestra cómo se deben realizar las actualizaciones en las filas modificadas estableciendo de forma explícita el comando UpdateCommand de DataAdapter. Observe cómo el parámetro especificado en la cláusula WHERE de la instrucción UPDATE tiene el valor adecuado para usar el valor Original de SourceColumn. Este hecho es muy importante ya que el valor Current puede haber sido modificado de forma que ya no coincida con el valor del origen de datos. El valor Original es el que se usó para llenar la tabla DataTable a partir del origen de datos.

SqlClient

Dim catDA As SqlDataAdapter = New SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " & _
                                     "WHERE CategoryID = @CategoryID", nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName")

Dim workParm As SqlParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")  

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"

catDA.Update(catDS)
[C#]
SqlDataAdapter catDA = new SqlDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);       

catDA.UpdateCommand = new SqlCommand("UPDATE Categories SET CategoryName = @CategoryName " +
                                     "WHERE CategoryID = @CategoryID" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", SqlDbType.NVarChar, 15, "CategoryName");

SqlParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", SqlDbType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");   

DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";

catDA.Update(catDS);

OleDb

Dim catDA As OleDbDataAdapter = New OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)       

catDA.UpdateCommand = New OleDbCommand("UPDATE Categories SET CategoryName = ? " & _
                                       "WHERE CategoryID = ?" , nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName")

Dim workParm As OleDbParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")    

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)
cRow("CategoryName") = "New Category"

catDA.Update(catDS)
[C#]
OleDbDataAdapter catDA = new OleDbDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);            

catDA.UpdateCommand = new OleDbCommand("UPDATE Categories SET CategoryName = ? " +
                                       "WHERE CategoryID = ?" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", OleDbType.VarChar, 15, "CategoryName");

OleDbParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OleDbType.Integer);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");    

DataRow cRow = catDS.Tables["Categories"].Rows[0];
cRow["CategoryName"] = "New Category";
catDA.Update(catDS);

Odbc

Dim catDA As OdbcDataAdapter = New OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn)

catDA.UpdateCommand = New OdbcCommand("UPDATE Categories SET CategoryName = ? " & _
                                      "WHERE CategoryID = ?" , nwindConn)

catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName")

Dim workParm As OdbcParameter = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int)
workParm.SourceColumn = "CategoryID"
workParm.SourceVersion = DataRowVersion.Original

Dim catDS As DataSet = New DataSet
catDA.Fill(catDS, "Categories")    

Dim cRow As DataRow = catDS.Tables("Categories").Rows(0)

cRow("CategoryName") = "New Category"

Dim modRows() As DataRow = catDS.Tables("Categories").Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent)

catDA.Update(modRows)
[C#]
OdbcDataAdapter catDA = new OdbcDataAdapter("SELECT CategoryID, CategoryName FROM Categories", nwindConn);

catDA.UpdateCommand = new OdbcCommand("UPDATE Categories SET CategoryName = ? " +
                                      "WHERE CategoryID = ?" , nwindConn);

catDA.UpdateCommand.Parameters.Add("@CategoryName", OdbcType.VarChar, 15, "CategoryName");

OdbcParameter workParm = catDA.UpdateCommand.Parameters.Add("@CategoryID", OdbcType.Int);
workParm.SourceColumn = "CategoryID";
workParm.SourceVersion = DataRowVersion.Original;

DataSet catDS = new DataSet();
catDA.Fill(catDS, "Categories");    

DataRow cRow = catDS.Tables["Categories"].Rows[0];

cRow["CategoryName"] = "New Category";

DataRow[] modRows = catDS.Tables["Categories"].Select(null, null, DataViewRowState.ModifiedCurrent);
catDA.Update(modRows);

Columnas AutoIncrement

Si las tablas del origen de datos tienen columnas con incremento automático, puede llenar las columnas del DataSet con los valores generados por el origen de datos si devuelve el valor de incremento automático como un parámetro de salida de un procedimiento almacenado y lo asocia a una columna de la tabla, o mediante el evento RowUpdated del DataAdapter. Como ejemplo, vea Recuperar valores de identidad o de autonumeración.

Sin embargo, los valores del DataSet pueden perder la sincronización con los del origen de datos y producir resultados inesperados. Por ejemplo, supongamos una tabla que tiene una columna CustomerID usada como clave principal con incremento automático. Si agrega dos clientes nuevos en el DataSet, el sistema de incremento automático les asigna los valores de CustomerId 1 y 2. Cuando la segunda fila del cliente se pasa al método Update del DataAdapter, la fila recién agregada recibe el valor CustomerID de 1 (con incremento automático) en el origen de datos, que no coincide con el valor 2, del DataSet. Cuando el DataAdapter rellena la fila en el DataSet con el valor devuelto, se produce una infracción de restricción ya que la primera fila del cliente ya tiene un CustomerID con el valor 1.

Para evitar este problema, es aconsejable que, si se usan columnas con incremento automático en un origen de datos y en un DataSet, se cree la columna del DataSet con un valor de incremento (AutoIncrementStep) de -1 y un valor inicial (AutoIncrementSeed) de 0, además de comprobar que el origen de datos genera valores de identidad con incremento automático a partir de 1 y con incrementos positivos. De esta forma, el DataSet generará números negativos para los valores de incremento automático, que no estarán nunca en conflicto con los valores de incremento automático generados por el origen de datos. Otra opción consiste en usar columnas del tipo Guid en lugar de columnas con incremento automático. El algoritmo que genera los valores Guid no debe generar nunca el mismo Guid en el DataSet y en el origen de datos. Para obtener más información sobre cómo definir columnas en una DataTable, vea Definir el esquema de DataTable.

Orden de las inserciones, actualizaciones y eliminaciones

En algunas circunstancias, es importante el orden en que se envían al origen de datos los cambios realizados en el DataSet. Por ejemplo, si se actualiza el valor de una clave principal de una fila existente y se ha agregado una nueva fila con el nuevo valor de la clave principal, es importante que la actualización de la fila se procese antes que la inserción.

Puede usar el método Select del DataTable para devolver una matriz DataRow que sólo haga referencia a filas con un estado (RowState) determinado. A continuación, puede pasar la matriz DataRow devuelta al método Update del DataAdapter para que procese las filas modificadas. Al especificar un subconjunto de filas que modificar, puede controlar el orden en que se procesan las inserciones, actualizaciones y eliminaciones.

Por ejemplo, en el código siguiente se garantiza que en primer lugar se realizan en la tabla las eliminaciones de filas, después las actualizaciones y finalmente las inserciones.

Dim updTable As DataTable = custDS.Tables("Customers")

' First process deletes.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Deleted))

' Next process updates.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.ModifiedCurrent))

' Finally, process inserts.
custDA.Update(updTable.Select(Nothing, Nothing, DataViewRowState.Added))
[C#]
DataTable updTable = custDS.Tables["Customers"];

// First process deletes.
custDA.Update(updTable.Select(null, null, DataViewRowState.Deleted));

// Next process updates.
custDA.Update(updTable.Select(null, null, DataViewRowState.ModifiedCurrent));

// Finally, process inserts.
custDA.Update(updTable.Select(null, null, DataViewRowState.Added));

Vea también

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