Compartir a través de


Introducción a la simultaneidad de datos en ADO.NET

Actualización: noviembre 2007

Cuando varios usuarios intentan modificar datos al mismo tiempo, es necesario establecer controles para impedir que las modificaciones de un usuario influyan negativamente en las de otros. El sistema mediante el cual se controla lo que sucede en esta situación se denomina control de concurrencia.

Tipos de control de concurrencia

En general, existen tres formas comunes para administrar la concurrencia en una base de datos:

  • Control de concurrencia pesimista: una fila no está disponible para los usuarios desde el momento en que se obtiene el registro hasta que se actualiza en la base de datos.

  • Control de concurrencia optimista: una fila no está disponible para otros usuarios mientras los datos se estén actualizando. La actualización examina la fila de la base de datos y determina si se han realizado cambios. Si se intenta actualizar un registro que ya se ha modificado, se produce una infracción de concurrencia.

  • "El último gana": una fila no está disponible para otros usuarios mientras los datos se estén actualizando. Sin embargo, no se intenta comparar las actualizaciones con el registro original; simplemente, el registro se escribe, con la posibilidad de sobrescribir los cambios realizados por otros usuarios desde la última vez que se actualizaron los registros.

Concurrencia pesimista

Normalmente, la concurrencia pesimista se utiliza por dos razones. En primer lugar, a veces existe una gran contienda por los mismos registros. El costo de bloquear los datos es menor que el de deshacer los cambios cuando se producen conflictos de concurrencia.

La concurrencia pesimista es útil también en situaciones en las que no es conveniente que cambie el registro durante una transacción. Un buen ejemplo sería una aplicación de inventario. Consideremos que el representante de una compañía va a comprobar el inventario para un posible cliente. Lo normal sería bloquear el registro hasta que se generase un pedido, lo que, en líneas generales, identificaría el artículo con el estado de pedido y lo quitaría del inventario disponible. Si no se generase ningún pedido, el bloqueo se liberaría para que otros usuarios que comprobasen el inventario obtuviesen un recuento exacto del inventario disponible.

No obstante, el control de concurrencia pesimista no es posible en una arquitectura desconectada. Las conexiones se abren sólo el tiempo suficiente para leer los datos o actualizarlos, por lo que los bloqueos no se pueden mantener durante períodos de tiempo prolongados. Además, una aplicación que mantiene los bloqueos durante períodos de tiempo prolongados no es escalable.

Concurrencia optimista

En la concurrencia optimista, los bloqueos se establecen y mantienen sólo mientras se está teniendo acceso a la base de datos. Los bloqueos impiden que otros usuarios intenten actualizar registros en ese mismo instante. Los datos están siempre disponibles, excepto durante el momento preciso en que está teniendo lugar una actualización. Para obtener más información, vea Simultaneidad optimista (ADO.NET).

Cuando se intenta realizar una actualización, se compara la versión original de una fila modificada con la fila existente en la base de datos. Si las dos son diferentes, la actualización no se realiza debido a un error de concurrencia. En ese instante, es de su responsabilidad la reconciliación de las dos filas mediante su propia lógica de empresa.

El último gana

Con la técnica de "el último gana", no se realiza ninguna comprobación de los datos originales y la actualización simplemente se escribe en la base de datos. Es comprensible que se pueda producir el siguiente escenario:

  • El usuario A obtiene un registro de la base de datos.

  • El usuario B obtiene el mismo registro de la base de datos, lo modifica y devuelve a la base de datos el registro actualizado.

  • El usuario A modifica el registro "antiguo" y lo devuelve a la base de datos.

En el escenario anterior, los cambios que realizó el usuario B no los ve nunca el usuario A. Asegúrese de que esta situación sea aceptable si tiene previsto usar el planteamiento "el último gana" del control de concurrencia.

Control de concurrencia en ADO.NET y Visual Studio

ADO.NET y Visual Studio utilizan la concurrencia optimista porque la arquitectura de datos se basa en datos desconectados. Por ello, es necesario agregar lógica de empresa para resolver los problemas de la concurrencia optimista.

Si decide utilizar la concurrencia optimista, existen dos métodos generales para determinar si se han producido cambios: el planteamiento del número de versión (números de versión reales o marcas de fecha y hora) y el planteamiento de guardar todos los valores.

Planteamiento del número de versión

En el planteamiento del número de versión, el registro que se va a actualizar debe tener una columna que contenga una marca de fecha y hora o un número de versión. La marca de fecha y hora o el número de versión se guardan en el cliente cuando se lee el registro. Después, se hace que el valor forme parte de la actualización.

Una forma de controlar la concurrencia es actualizar sólo si el valor de la cláusula WHERE coincide con el valor del registro. La representación SQL de este planteamiento es:

UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2 
WHERE DateTimeStamp = @origDateTimeStamp

Alternativamente, se puede realizar la comparación con el número de versión:

UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2 
WHERE RowVersion = @origRowVersionValue

Si las marcas de fecha y hora o los números de versión coinciden, el registro del almacén de datos no ha cambiado y se puede actualizar de manera segura con los nuevos valores del conjunto de datos. Si no coinciden, se devuelve un error. Puede escribir código para implementar esta forma de comprobar la concurrencia en Visual Studio. También deberá escribir código para responder a los posibles conflictos de actualización. Para mantener la exactitud de la marca de fecha y hora o el número de versión, necesita configurar un desencadenador en la tabla para que se actualice cuando se produzca un cambio en alguna fila.

Planteamiento de guardar todos los valores

Una alternativa al uso de una marca de fecha y hora o un número de versión es obtener copias de todos los campos cuando se lea el registro. El objeto DataSet de ADO.NET mantiene dos versiones de cada registro modificado: una versión original (la que se leyó originalmente del origen de datos) y una versión modificada que representa las actualizaciones del usuario. Cuando se intenta volver a escribir el registro en el origen de datos, se comparan los valores originales de la fila de datos y el registro del origen de datos. Si coinciden, significa que el registro de la base de datos no ha cambiado desde que se leyó. En ese caso, los valores modificados del conjunto de datos se escriben en la base de datos sin problemas.

Cada uno de los cuatro comandos del adaptador de datos (DELETE, INSERT, SELECT y UPDATE) tiene una colección de parámetros. Cada comando tiene parámetros para los valores originales y para los valores actuales (o modificados).

Nota:

Cuando se agregan registros nuevos (comando INSERT), sólo se requieren los valores actuales, ya que no existe ningún registro original; cuando se quitan registros (comando DELETE), sólo se requieren los valores originales para poder localizar el registro que se desea eliminar.

En el ejemplo siguiente se muestra el texto de un comando del conjunto de datos que actualiza una tabla Customers típica. El comando se especifica para SQL dinámico y para la concurrencia optimista.

UPDATE Customers SET CustomerID = @currCustomerID, CompanyName = @currCompanyName, ContactName = @currContactName,
       ContactTitle = currContactTitle, Address = @currAddress, City = @currCity, 
       PostalCode = @currPostalCode, Phone = @currPhone, Fax = @currFax
WHERE (CustomerID = @origCustomerID) AND (Address = @origAddress OR @origAddress IS NULL AND Address IS NULL) AND (City = @origCity OR @origCity IS NULL AND City IS NULL)
      AND (CompanyName = @origCompanyName OR @origCompanyName IS NULL AND CompanyName IS NULL) AND (ContactName = @origContactName OR @origContactName IS NULL AND ContactName IS NULL) AND (ContactTitle = @origContactTitle OR @origContactTitle IS NULL AND ContactTitle IS NULL) 
      AND (Fax = @origFax OR @origFax IS NULL AND Fax IS NULL) AND (Phone = @origPhone OR @origPhone IS NULL AND Phone IS NULL) AND (PostalCode = @origPostalCode OR @origPostalCode IS NULL AND PostalCode IS NULL);
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City,
       PostalCode, Phone, Fax
FROM Customers WHERE (CustomerID = @currCustomerID)

Tenga en cuenta que los nueve parámetros de la instrucción SET representan los valores actuales que se escribirán en la base de datos, mientras que los nueve parámetros de la instrucción WHERE representan los valores originales que se utilizan para encontrar el registro original.

Los nueve primeros parámetros de la instrucción SET se corresponden con los nueve primeros parámetros de la colección de parámetros. Estos parámetros tendrían su propiedad SourceVersion establecida en Current.

Los nueve parámetros siguientes de la instrucción WHERE se utilizan para la concurrencia optimista. Estos marcadores de posición se corresponderían con los nueve parámetros siguientes de la colección de parámetros, y cada uno de estos parámetros tendría la propiedad SourceVersion establecida en Original.

La instrucción SELECT se utiliza para actualizar el conjunto de datos después de que se haya producido la actualización. Se genera cuando se establece la opción Actualizar el conjunto de datos del cuadro de diálogo Opciones de generación SQL avanzadas.

Nota:

En la instrucción SQL anterior se utilizan parámetros con nombre, mientras que los comandos OleDbDataAdapter usan signos de interrogación (?) como marcadores de posición de los parámetros.

De manera predeterminada, Visual Studio crea estos parámetros automáticamente si selecciona la opción Concurrencia optimista en el Asistente para la configuración del adaptador de datos. Debe decidir si desea agregar código para controlar los errores basándose en las necesidades de su empresa. ADO.NET proporciona un objeto DBConcurrencyException que devuelve la fila que infringe las reglas de concurrencia. Para obtener más información, vea Cómo: Controlar errores de simultaneidad.

Vea también

Tareas

Cómo: Controlar errores de simultaneidad

Conceptos

Simultaneidad optimista (ADO.NET)

Referencia

DBConcurrencyException