Foros del Web » Programando para Internet » ASPX (.net) »

Concurrencia en ado.net

Estas en el tema de Concurrencia en ado.net en el foro de ASPX (.net) en Foros del Web. Tengo una duda acerca de la concurrencia en .NET trabajo con el modelo desconectado, estoy usando access y uso OLEDB Según tengo entendido, ADO.NET carga ...
  #1 (permalink)  
Antiguo 15/01/2015, 15:50
 
Fecha de Ingreso: agosto-2010
Mensajes: 45
Antigüedad: 13 años, 8 meses
Puntos: 0
Concurrencia en ado.net

Tengo una duda acerca de la concurrencia en .NET trabajo con el modelo desconectado, estoy usando access y uso OLEDB
Según tengo entendido, ADO.NET carga el DataSet y se desconecta hasta que sea nuevamente necesaria una conexion.
No hay mucha infromación en internet sobre este problema, por ejemplo, me planteo esta dificultad:

1) 2 usuarios distintos en 2 maquinas diferentes acceden al mismo registro
2) cargo los datos en un DataSet
3) maquina1 modifica el campo1 en el DataSet incrementando en 2 el valor del campo1. Luego actualiza los datos en la bd fisica.
4) maquina2, que cargo los datos en el Dataset mucho antes que maquina1 ahora tendría los datos desactualizados en su DataSet y va a sumar en 1 el campo1 pensando que campo1 es igual a 1, y en realidad campo1 ya vale 2.

como se puede solucionar esto? Necesitaria un ejemplo si es posible, Gracias
  #2 (permalink)  
Antiguo 16/01/2015, 12:18
Avatar de leodp77  
Fecha de Ingreso: marzo-2013
Mensajes: 39
Antigüedad: 11 años, 1 mes
Puntos: 2
Respuesta: Concurrencia en ado.net

Actualizá la tabla (ubicada en el dataset con los datos de la db), que esta enlazada al control que muestra los datos, cada x segundos con la clase timer o despues de cada actualización para que vean siempre el dato actual.

Timer:
Implementa un temporizador que provoca un evento a intervalos definidos por el usuario. Este temporizador optimizado para el uso en aplicaciones de Windows Forms y se debería utilizar en una ventana.

http://msdn.microsoft.com/es-es/library/system.windows.forms.timer%28v=vs.110%29.aspx


Código C++:
Ver original
  1. private Timer timer1;
  2. public void InitTimer()
  3. {
  4.     timer1 = new Timer();
  5.     timer1.Tick += new EventHandler(timer1_Tick);
  6.     timer1.Interval = 2000; // in miliseconds
  7.     timer1.Start();
  8. }
  9.  
  10. private void timer1_Tick(object sender, EventArgs e)
  11. {
  12.     this.providersTableAdapter.Fill(paolaStockDataSet.Providers);
  13. }

ADO.NET utiliza manejo de concurrencia optimista porque "trabaja" con datos desconectados. Es un dataset con distintas tablas dentro, se actualiza la tabla, y luego se sincroniza con la bd fisica. Mientras se actualiza la bd se lockea el registro que está siendo modificado. Cuando se quiere actualizar el registro se comparan las versiones. Si las mismas son diferentes, da error de concurrencia. Podés agregarle tu logica de negocios para mejorar el control de versiones. En el siguiente enlace se explica como hacerlo con codigo SQL.

Diferentes formas de manejar la concurrencia:
http://msdn.microsoft.com/en-us/library/cs6hb8k4%28v=VS.80%29.aspx
__________________
"La mejor forma de obtener información correcta de los foros de internet es enviar algo incorrecto y esperar las correcciones"
-- Matthew Austern

Última edición por leodp77; 16/01/2015 a las 14:29
  #3 (permalink)  
Antiguo 17/01/2015, 15:11
 
Fecha de Ingreso: agosto-2010
Mensajes: 45
Antigüedad: 13 años, 8 meses
Puntos: 0
Respuesta: Concurrencia en ado.net

Gracias leodp77, pero lo que yo pregunto es otra cosa absolutamente distinta, ademas de lo que pregunto, otro escenario parecido seria por ejemplo, en un entorno multiusuario, donde por ejemplo, PC1, antes de ejecutar una sentencia sql UPDATE para actualizar muchos registros, necesita saber si PC2 hizo algun cambio mientras la PC1 trabajaba con el dataset de manera desconectada, y en caso de haber sucedido esto, PC1 cancela la actualizacion, informa que alguno de los registros fueron modificados y vuelve a actualzar el dataset con la informacion nueva. como se implementaria en la practica esto??
  #4 (permalink)  
Antiguo 18/01/2015, 15:25
Avatar de leodp77  
Fecha de Ingreso: marzo-2013
Mensajes: 39
Antigüedad: 11 años, 1 mes
Puntos: 2
Respuesta: Concurrencia en ado.net

Entiendo. Dejando en claro que se comparan las versiones al enviar la consulta a la bd, veamos lo siguiente.

Vos necesitás hacerle saber al usuario de la PC1 que hubo una actualización entre el momento en el que abrió el registro y la confirmación de actualización desde PC1. Esto lo podés hacer con una vista previa, mostrándole como quedaría el dato, y pidiendo confirmación. Si el registro está ocupado, te tira error de concurrencia, y lo podés capturar con DBConcurrencyException, y actualizando el dataset mediante el tableAdapter. Ahí te tiré dos ideas de como resolverlo.

En el tutorial que te pasé hay código para implementar la lógica de negocios que te pasé.
__________________
"La mejor forma de obtener información correcta de los foros de internet es enviar algo incorrecto y esperar las correcciones"
-- Matthew Austern
  #5 (permalink)  
Antiguo 18/01/2015, 22:14
 
Fecha de Ingreso: agosto-2010
Mensajes: 45
Antigüedad: 13 años, 8 meses
Puntos: 0
Respuesta: Concurrencia en ado.net

Hola Leo, Gracias por contestar, pero lo que yo digo es otra cosa. En principio no es una aplicacion Web, sino una aplicacion Windows con Visual Basic.NET, trabajo con datasets, en modo desconectado y con OLEDB Y ACCESS luego, este dataset, antes de guardarse en la base de datos tiene que mirar si hubo un cambio desde que se cargo, hasta que se presiono el boton "guardar", es simple el escenario, es asi:

a) maquina 1 y maquina 2 llenan un dataset en el evento load y este evento load muestra los datos de una base de datos en unos cuadros de textos. b) maquina 1 llena los cuadros de texto y hace click en en boton guardar, aca los datos tendrian que guardarse en la base de datos. c)maquina 2 hace lo mismo, pero ya la base de datos cambio, por lo tanto los cuadros de texto reflejan incorrectamente los valores de la base de datos, entonces, cuando maquina 2 modifica los cuadros de texto y hace click en el boton guardar, aca tiene que lanzarse la excepcion, diciendo que la base de datos cambio, o sea, se trata simplemente de verificar si cambio el origen de los datos e informar, nada mas.
EL resgistro no estaria ocupado en ningun momento. solo que cambio y eso lo tiene que saber maquina 1 antes de hacer un cambio en cualquier campo del mismo. O sea, maquina 1 esta viendo informacion en un dataset que no esta para nada actualizado, y yo no quiero que modifique un registro sin saber que ese registro ya fue modificado., la idea es que cuando maquina 1 presione el boton guardar informe que cambio el registro y que vuelva a llenar el dataset con la informacion nueva, y que no deje guardar nada. Pero lo quiero hacer con la sentencia UPDATE de SQL. y si no me equicoco, el evento para caputrarlo se llama "RowUpdated" (evento del OleDbDataAdapter pero no se como seria el codigo. Gracias

todo esto quiero hacerlo con la sentencia UPDATE de Sql, aunque haya otra forma mas simple de hacerlo. Es decir, con la sentencia "update tabla set campo=..."

Última edición por micase; 18/01/2015 a las 22:28
  #6 (permalink)  
Antiguo 18/01/2015, 22:37
 
Fecha de Ingreso: agosto-2010
Mensajes: 45
Antigüedad: 13 años, 8 meses
Puntos: 0
Respuesta: Concurrencia en ado.net

Te escribi un mensaje mas arriba, el codigo imagino que es similar a este que te escribo mas abajo, que no da ningun error pero no funciona para nada, no se guardan los datos en la base de datos y tampoco se desencadena el evento rowupdate. Es todo como si nada.


Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Data.OleDb.OleDbCommandBuilder
Imports System.Data.SqlClient

Public Class Form1
Public con As New OleDbConnection
Public da As OleDbDataAdapter
Public ds As New DataSet()
Public daUpdateCommand As OleDbCommand
Public dt As DataTable
Public Event RowUpdated As SqlRowUpdatedEventHandler
Public Event RowUpdating As OleDbRowUpdatingEventHandler
Private connetionString As String
Private connection As OleDbConnection
Private adapter As OleDbDataAdapter
Private parameter As OleDbParameter
Private dataset As New DataSet


Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

Try
connetionString = "PROVIDER=Microsoft.ACE.OLEDB.12.0;Data Source=C:\concurrencia\base.accdb;"
connection = New OleDbConnection(connetionString)
connection.Open()
adapter = New OleDbDataAdapter("SELECT * from tabla2", connection)
adapter.Fill(DataSet, "tabla2")


Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub


Sub OnRowUpdated(ByVal sender As Object, ByVal args As OleDbRowUpdatedEventArgs)
If args.RecordsAffected = 0 Then
args.Row.RowError = "Violacion concurrencia optimista"
args.Status = UpdateStatus.SkipCurrentRow
End If
End Sub






ACA VIENE button1_click que seria el boton que guarda la informacion de los cuadros de texto a la base de datos

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Try
Dim comando As OleDbCommand = connection.CreateCommand()
adapter.UpdateCommand = New OleDbCommand("UPDATE tabla2 (nombre, apellido) VALUES(@nombre, @apellido) WHERE nombre = @oldnombre AND apellido = @oldapellido", connection)

adapter.UpdateCommand.Parameters.Add("@nombre", OleDbType.VarChar, 255).Value = txtID.Text
adapter.UpdateCommand.Parameters.Add("@apellido", OleDbType.VarChar, 255).Value = txtCharData.Text

parameter = adapter.UpdateCommand.Parameters.Add("@oldnombre", OleDbType.VarChar, 255, "nombre")
parameter.SourceVersion = DataRowVersion.Original
parameter = adapter.UpdateCommand.Parameters.Add("@oldapellido ", OleDbType.VarChar, 255, "apellido")
parameter.SourceVersion = DataRowVersion.Original




AddHandler adapter.RowUpdated, New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)



adapter.Update(dataset, "tabla2")
adapter.Fill(dataset, "tabla2")


Dim dataRow As DataRow

For Each dataRow In dataSet.Tables("tabla2").Rows

If dataRow.HasErrors Then
Console.WriteLine(dataRow(0) & vbCrLf & dataRow.RowError)
End If
Next



connection.Close()
Catch ex As Exception
End Try
End Sub
  #7 (permalink)  
Antiguo 21/01/2015, 18:41
Avatar de leodp77  
Fecha de Ingreso: marzo-2013
Mensajes: 39
Antigüedad: 11 años, 1 mes
Puntos: 2
Respuesta: Concurrencia en ado.net

Si lo que ocurre es lo siguiente, entendí el problema en tu primer mensaje:
La aplicación se ejecuta en un equipo 1 y en un equipo 2. Un usuario en el equipo 1 abre la aplicación, ve los datos, y procede a modificar una fila de la tabla. Antes de enviar los datos a la base de datos, otro usuario en el equipo 2 modifica la base de datos mediante el mismo programa. En el equipo 1, luego de presionar el botón Guardar para enviar los datos a la base de datos, el programa lanza una excepción DBConcurrencyException, al ejecutar UpdateCommand.

Para controlar esta excepcion, lo unico que tenés que hacer es lo siguiente:

Código vb:
Ver original
  1. Try
  2.             Me.TableAdapterManager.UpdateAll(Me.TestingDBDataSet)
  3.         Catch ex As DBConcurrencyException
  4.             MessageBox.Show("Error de concurrencia")
  5. End Try

El evento completo quedaría así:

Código vb:
Ver original
  1. Private Sub UsuariosBindingNavigatorSaveItem_Click(sender As Object, e As EventArgs) Handles UsuariosBindingNavigatorSaveItem.Click
  2.  
  3.         Me.Validate()
  4.         Me.UsuariosBindingSource.EndEdit()
  5.         Try
  6.             Me.TableAdapterManager.UpdateAll(Me.TestingDBDataSet)
  7.         Catch ex As DBConcurrencyException
  8.             MessageBox.Show("Error de concurrencia")
  9.         End Try
  10.  
  11.         Me.UsuariosTableAdapter.Fill(Me.TestingDBDataSet.Usuarios)
  12.  
  13.     End Sub

Obviamente es conveniente capturar todas las excepciones que se puedan lanzar, podés incluso poner tantos catch como quieras, para controlar cada excepción.
El objeto UsuariosBindingNavigatorSaveItem representaría al botón guardar.

El form quedaría algo así.
__________________
"La mejor forma de obtener información correcta de los foros de internet es enviar algo incorrecto y esperar las correcciones"
-- Matthew Austern

Última edición por leodp77; 21/01/2015 a las 18:49
  #8 (permalink)  
Antiguo 22/01/2015, 00:48
 
Fecha de Ingreso: agosto-2010
Mensajes: 45
Antigüedad: 13 años, 8 meses
Puntos: 0
Respuesta: Concurrencia en ado.net

Hola Leo, gracias, pero en realidad yo necesito que al hacer click en un boton comun, se compare todas las filas de mi dataset con la tabla (saber si se modificaron) pero todos los datos del dataset, y no con un control de esos, sino que simplemente haciendo un click en un boton cualquiera, y si es posible, actualizar solo aquellos registros que no fueron modificados. Por eso decia, si no me equivoco se hace con el codigo que puse yo, aunque no esta completo. Asi como en Sql Server se hace con un campo Timestamp con acces si no me equivoco se hace como decia yo, tenes alguna idea? gracias

Última edición por micase; 22/01/2015 a las 00:58
  #9 (permalink)  
Antiguo 22/01/2015, 08:57
Avatar de leodp77  
Fecha de Ingreso: marzo-2013
Mensajes: 39
Antigüedad: 11 años, 1 mes
Puntos: 2
Respuesta: Concurrencia en ado.net

Internamente hace lo que comentás.

Si querés usar un objeto Button en vez de un BindingNavigatorSaveItem, meté el código que te pasé en el evento Click del Button. Si querés podés ocultar el BindingNavigator, y te queda como querés.

Ahora observando tu código tenemos lo siguiente:

Código vb:
Ver original
  1. adapter.Update(dataset, "tabla2")
  2. adapter.Fill(dataset, "tabla2")

Acá debés capturar la excepción DBConcurrencyException. Quedaría así:

Código vb:
Ver original
  1. Try
  2. adapter.Update(dataset, "tabla2")
  3. adapter.Fill(dataset, "tabla2")
  4.         Catch ex As DBConcurrencyException
  5.             MessageBox.Show("Error de concurrencia")
  6.         End Try

Código vb:
Ver original
  1. Sub OnRowUpdated(ByVal sender As Object, ByVal args As OleDbRowUpdatedEventArgs)
  2. If args.RecordsAffected = 0 Then
  3. args.Row.RowError = "Violacion concurrencia optimista"
  4. args.Status = UpdateStatus.SkipCurrentRow
  5. End If
  6. End Sub
Esto está mal. El mensaje sería "0 registros afectados.".
Incluso no capturás ninguna excepción.

Al ejecutar la aplicación, y probarla, modifica la tabla, y antes de presionar el botón Guardar, modificá la base de datos (ubicada en Projecto/bin/debug o Projecto/bin/release). Porque si modificás la BD original estarías errándole de entrada en el testing.

PD: Si estás haciendo una App nueva, te diría que utilices los controles/objetos que te comenté (puro autogenerado, en 1 minuto tenés el formulario hecho). No reinventés la rueda.
__________________
"La mejor forma de obtener información correcta de los foros de internet es enviar algo incorrecto y esperar las correcciones"
-- Matthew Austern
  #10 (permalink)  
Antiguo 23/01/2015, 14:34
 
Fecha de Ingreso: agosto-2010
Mensajes: 45
Antigüedad: 13 años, 8 meses
Puntos: 0
Respuesta: Concurrencia en ado.net

esta bien, pero nunca se desencadena rowupdate porque en la sentencia :

parameter = adapter.UpdateCommand.Parameters.Add("@oldnombre", OleDbType.VarChar, 255, "nombre")

@oldnombre nunca toma valores, lo que no se como hacer es que @oldnombre tome los valores viejos de la base de datos, y lo tiene que hacer por cada registro de mi dataset.

Etiquetas: ado.net, concurrencia, dataset
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 19:22.