Pregunta

Tenemos una aplicación que fue escrito originalmente como una aplicación de escritorio, he aquí hace muchos años. Se inicia una transacción siempre que se abre una pantalla de edición, y las confirmaciones si se hace clic en OK, o retrotrae si hace clic en Cancelar. Esto funcionó bien para una aplicación de escritorio, pero ahora estamos tratando de mover a ADO.NET y SQL Server, y las transacciones de larga ejecución son problemáticos.

Me encontré con que vamos a tener un problema cuando varios usuarios están tratando de editar (diferentes subconjuntos de) la misma mesa al mismo tiempo. En nuestra base de datos anterior, las transacciones de cada usuario podría adquirir bloqueos a nivel de registro a cada registro se modificaron durante su operación; ya que diferentes usuarios se editan diferentes registros, todo el mundo tiene su propio cerraduras y todo funciona. Sin embargo, en SQL Server, tan pronto como un usuario edita un registro dentro de una transacción, SQL Server aparece para obtener un bloqueo en el toda la tabla. cuando un segundo usuario intenta editar un registro diferente en la misma mesa , app del segundo usuario simplemente se bloquea, porque los bloques SqlConnection hasta el primer usuario o bien confirma o deshace.

Soy consciente de que las transacciones de larga ejecución son malas, y sé que el mejor solución sería cambiar estas pantallas por lo que ya no mantienen transacciones abiertas durante mucho tiempo. Pero ya que eso significaría algunos cambios invasivos y riesgosos, también quiero investigar si hay una manera de conseguir este código en funcionamiento tal y como son, simplemente, así que sé cuáles son mis opciones.

¿Cómo puedo obtener las transacciones de los dos usuarios diferentes en SQL Server para bloquear registros individuales en lugar de toda la tabla?

Aquí hay una aplicación de consola y sucio rápida que ilustra el tema. He creado una base de datos llamada "test1", con una tabla llamada "valores" que sólo tiene ID (int) y el valor nvarchar (columnas). Si ejecuta la aplicación, se pide un ID para modificar, inicia una transacción, modifica ese registro, y luego deja al descubierto la transacción hasta que se pulsa ENTER. Quiero ser capaz de

  1. iniciar el programa y decirle que la actualización ID 1;
  2. permitan que se quede su transacción y modificar el registro;
  3. iniciar una segunda copia del programa y decirle que la actualización de ID 2;
  4. tiene que poner al día (y confirma), mientras que la primera transacción de aplicación está todavía abierta.

Actualmente se congela en el paso 4, hasta que volver a la primera copia de la aplicación y cerca de ella o pulse ENTER para que se comprometa. La llamada a command.ExecuteNonQuery cuadras hasta la primera conexión se cierra.

public static void Main()
{
    Console.Write("ID to update: ");
    var id = int.Parse(Console.ReadLine());
    Console.WriteLine("Starting transaction");
    using (var scope = new TransactionScope())
    using (var connection = new SqlConnection(@"Data Source=localhost\sqlexpress;Initial Catalog=test1;Integrated Security=True"))
    {
        connection.Open();
        var command = connection.CreateCommand();
        command.CommandText = "UPDATE [Values] SET Value = 'Value' WHERE ID = " + id;
        Console.WriteLine("Updating record");
        command.ExecuteNonQuery();
        Console.Write("Press ENTER to end transaction: ");
        Console.ReadLine();
        scope.Complete();
    }
}

Aquí hay algunas cosas que ya he intentado, sin cambio en el comportamiento:

  • Cambiar el nivel de aislamiento en "leer no comprometidos"
  • Especificación de un "CON (ROWLOCK)" en la instrucción UPDATE
¿Fue útil?

Solución

Sólo comprobación, pero ¿tiene una clave principal o índice único en la columna ID?

Otros consejos

Mira en optimistas en comparación con el bloqueo pesimista.

Editar: artículo anterior relacionado con ADO clásico ... lo siento.

http://msdn.microsoft.com/ en-us / library / cs6hb8k4 (VS.71) .aspx

Probablemente el índice fue creado con bloqueos de registro establecidos en "off".
"CON (ROWLOCK)" en una consulta no tendría ningún efecto en ese caso.

Puede volver a activarlos con ALTER INDEX , por ejemplo, :

ALTER INDEX [PK_Values] ON [Values] SET (ALLOW_ROW_LOCKS = ON)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top