Tutorial: Ampliar la memoria caché de base de datos local para admitir la sincronización bidireccional
Actualización: Julio de 2008
En Visual Studio 2008, la memoria caché de base de datos local configura una base de datos de SQL Server Compact 3.5 y un conjunto de clases parciales que habilitan Microsoft Synchronization Services para ADO.NET. Dado que Visual Studio genera clases parciales, puede escribir código para agregar la funcionalidad de sincronización y mantener la capacidad para ver y cambiar los valores en el cuadro de diálogo Configurar sincronización de datos. Para obtener más información acerca de las clases parciales, vea Cómo: Dividir una clase en clases parciales.
De forma predeterminada, el cuadro de diálogo Configurar sincronización de datos permite configurar Servicios de sincronización únicamente para la descarga. Esto significa que después de configurar la sincronización de datos, la llamada a Synchronize() solo descargará los cambios del servidor en la base de datos cliente. Uno de las maneras más comunes de ampliar el código de sincronización consiste en configurar la sincronización bidireccional. Esto permite cargar los cambios del cliente en el servidor. Para habilitar la sincronización bidireccional, se recomienda ampliar el código generado de las maneras siguientes:
Establezca la dirección de sincronización en bidireccional.
Agregue código para controlar los conflictos de sincronización.
Quite las columnas de seguimiento del servidor en los comandos de sincronización.
Requisitos previos
Antes de iniciar este tutorial, debe completar el Tutorial: Crear una aplicación que se conecta ocasionalmente. Después de completar dicho tutorial, dispondrá de un proyecto que contiene una memoria caché de base de datos local y una aplicación de Windows Forms que permite descargar los cambios de la tabla Customers de Northwind en una base de datos de SQL Server Compact. Ahora ya está preparado para cargar esta solución de tutorial y agregar funcionalidad bidireccional.
Nota: |
---|
Es posible que su equipo muestre nombres o ubicaciones diferentes para algunos de los elementos de la interfaz de usuario de Visual Studio incluidos en las instrucciones siguientes. La edición de Visual Studio que se tenga y la configuración que se utilice determinan estos elementos. Para obtener más información, vea Valores de configuración de Visual Studio. |
Para abrir la solución OCSWalkthrough
Abra Visual Studio.
En el menú Archivo, abra una solución o proyecto existente y busque la solución OCSWalkthrough. Es el archivo OCSWalkthrough.sln.
Establecer la dirección de sincronización
El cuadro de diálogo Configurar sincronización de datos permite establecer la propiedad SyncDirection() en DownloadOnly() o Snapshot(). Para habilitar la sincronización bidireccional, establezca la propiedad SyncDirection() en Bidirectional() para cada tabla que desee habilitar para la carga de cambios.
Para establecer la dirección de sincronización
Haga clic con el botón secundario en NorthwindCache.sync y haga clic en Ver código. La primera vez que lleva a cabo esta acción, Visual Studio crea un archivo NorthwindCache bajo el nodo NorthwindCache.sync en el Explorador de soluciones. Este archivo contiene una clase parcial NorthwindCacheSyncAgent y en él puede agregar otras clases según sea necesario.
En el archivo de clases NorthwindCache, agregue código para que el método NorthwindCacheSyncAgent.OnInitialized() se parezca al código siguiente:
partial void OnInitialized() { this.Customers.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional; }
Private Sub OnInitialized() Me.Customers.SyncDirection = Microsoft.Synchronization.Data.SyncDirection.Bidirectional End Sub
Abra Form1 en el Editor de código.
En el archivo Form1, modifique la línea de código en el controlador de eventos SynchronizeButton_Click para que incluya estadísticas de carga y descarga:
MessageBox.Show("Changes downloaded: " + syncStats.TotalChangesDownloaded.ToString() + Environment.NewLine + "Changes uploaded: " + syncStats.TotalChangesUploaded.ToString());
MessageBox.Show("Changes downloaded: " & _ syncStats.TotalChangesDownloaded.ToString & _ Environment.NewLine & _ "Changes uploaded: " & _ syncStats.TotalChangesUploaded.ToString)
Probar la aplicación
Ahora la aplicación está configurada para realizar cargas y descargas durante la sincronización.
Para probar la aplicación
Presione F5.
En el formulario, actualice un registro y, a continuación, haga clic en el botón Guardar (el icono de disco en la barra de herramientas).
Haga clic en Sincronizar ahora.
Aparecerá un cuadro de mensaje que contiene información sobre los registros sincronizados. Las estadísticas muestran que se cargó una fila y se descargó otra, aunque en el servidor no se ha realizado ningún cambio. La descarga adicional se produce porque los cambios en el cliente se devuelven a éste tras su aplicación en el servidor. Para obtener más información, vea "Determining Which Client Made a Data Change" en How to: Use a Custom Change Tracking System.
Haga clic en Aceptar para cerrar el cuadro de mensaje, pero deje la aplicación en ejecución.
Ahora cambiará el mismo registro en el cliente y en el servidor para crear un conflicto (una infracción de simultaneidad) durante la sincronización.
Para probar la aplicación y crear un conflicto
En el formulario, actualice un registro y, a continuación, haga clic en el botón Guardar.
Con la aplicación todavía en ejecución, utilice el Explorador de servidores/Explorador de bases de datos (u otra herramienta de administración de bases de datos) para conectar a la base de datos servidor.
Para mostrar el comportamiento predeterminado en la resolución de conflictos, en el Explorador de servidores/Explorador de bases de datos, actualice el mismo registro que actualizó en el formulario, pero cámbielo esta vez a un valor diferente y confirme el cambio. (Desplácese fuera de la fila modificada).
Regrese al formulario y haga clic en Sincronizar ahora.
Compruebe la actualización en la cuadrícula de la aplicación y la base de datos servidor. Observe que la actualización que realizó en el servidor ha sobrescrito la actualización en el cliente. Para obtener información sobre cómo cambiar este comportamiento de la resolución de conflictos, vea la sección siguiente de este tema, "Agregar código para controlar conflictos de sincronización".
Agregar código para controlar conflictos de sincronización
En Servicios de sincronización, una fila está en conflicto si se ha cambiado tanto en el cliente como en el servidor entre las sincronizaciones. Servicios de sincronización proporciona un conjunto de características que se pueden utilizar para detectar y resolver los conflictos. En esta sección, agregará el control básico para los conflictos en los que se actualiza la misma fila tanto en el cliente como en el servidor. Otros tipos de conflictos incluyen una fila que se elimina en una base de datos y se actualiza en la otra, o filas con claves principales duplicadas que se insertan en ambas bases de datos. Para obtener más información sobre la detección y resolución de conflictos, vea How to: Handle Data Conflicts and Errors.
Nota: |
---|
El código proporciona un ejemplo básico de control de conflictos. La manera en la que se controlan los conflictos depende de los requisitos de la aplicación y de la lógica de negocios. |
Agregue código para controlar el evento ApplyChangeFailed del servidor y el evento ApplyChangeFailed del cliente. Estos eventos se provocan cuando no se puede aplicar una fila debido a un conflicto o un error. Los métodos que controlan estos eventos comprueban el tipo de conflicto y especifican que los conflictos de actualización de cliente o actualización de servidor se deben resolver obligando a que el cambio del cliente se escriba en la base de datos servidor. El comando de sincronización que aplica las actualizaciones en la base de datos servidor incluye lógica para reconocer cuándo se debe forzar un cambio. Este comando está incluido en el código de la siguiente sección de este tema, "Quitar las columnas de seguimiento del servidor de los comandos de sincronización".
Los pasos que se llevan a cabo para agregar control de conflictos varían en función de si se utiliza C# o Visual Basic.
Para agregar control de conflictos
Si utiliza C#, agregue código a NorthwindCache.cs y Form1.cs. En NorthwindCache.cs, agregue el código siguiente después del final de la clase NorthwindCacheSyncAgent:
public partial class NorthwindCacheServerSyncProvider { partial void OnInitialized() { this.ApplyChangeFailed += new System.EventHandler<Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs> (NorthwindCacheServerSyncProvider_ApplyChangeFailed); } private void NorthwindCacheServerSyncProvider_ApplyChangeFailed(object sender, Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs e) { if (e.Conflict.ConflictType == Microsoft.Synchronization.Data.ConflictType.ClientUpdateServerUpdate) { // Resolve a client update / server update conflict by force writing // the client change to the server database. System.Windows.Forms.MessageBox.Show("A client update / server update conflict " + "was detected at the server."); e.Action = Microsoft.Synchronization.Data.ApplyAction.RetryWithForceWrite; } } } public partial class NorthwindCacheClientSyncProvider { public void AddHandlers() { this.ApplyChangeFailed += new System.EventHandler<Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs> (NorthwindCacheClientSyncProvider_ApplyChangeFailed); } private void NorthwindCacheClientSyncProvider_ApplyChangeFailed(object sender, Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs e) { if (e.Conflict.ConflictType == Microsoft.Synchronization.Data.ConflictType.ClientUpdateServerUpdate) { // Resolve a client update / server update conflict by keeping the // client change. e.Action = Microsoft.Synchronization.Data.ApplyAction.Continue; } } }
En Form1.cs, modifique el código del controlador de eventos SynchronizeButton_Click para que llame al método AddHandler que agregó a NorthwindCache.cs en el paso anterior:
NorthwindCacheSyncAgent syncAgent = new NorthwindCacheSyncAgent(); NorthwindCacheClientSyncProvider clientSyncProvider = (NorthwindCacheClientSyncProvider)syncAgent.LocalProvider; clientSyncProvider.AddHandlers(); Microsoft.Synchronization.Data.SyncStatistics syncStats = syncAgent.Synchronize();
Si utiliza Visual Basic, en NorthwindCache.vb, agregue el código siguiente después de la instrucción End Class de la clase NorthwindCacheSyncAgent.
Partial Public Class NorthwindCacheServerSyncProvider Private Sub NorthwindCacheServerSyncProvider_ApplyChangeFailed( _ ByVal sender As Object, ByVal e As _ Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs) _ Handles Me.ApplyChangeFailed If e.Conflict.ConflictType = _ Microsoft.Synchronization.Data.ConflictType.ClientUpdateServerUpdate Then ' Resolve a client update / server update conflict by force writing ' the client change to the server database. MessageBox.Show("A client update / server update" & _ " conflict was detected at the server.") e.Action = Microsoft.Synchronization.Data.ApplyAction.RetryWithForceWrite End If End Sub End Class Partial Public Class NorthwindCacheClientSyncProvider Private Sub NorthwindCacheClientSyncProvider_ApplyChangeFailed( _ ByVal sender As Object, ByVal e As _ Microsoft.Synchronization.Data.ApplyChangeFailedEventArgs) _ Handles Me.ApplyChangeFailed If e.Conflict.ConflictType = _ Microsoft.Synchronization.Data.ConflictType.ClientUpdateServerUpdate Then ' Resolve a client update / server update conflict by keeping the ' client change. e.Action = Microsoft.Synchronization.Data.ApplyAction.Continue End If End Sub End Class
Para sincronizar y ver la resolución de conflictos
Presione F5.
En el formulario, actualice un registro y, a continuación, haga clic en el botón Guardar.
En el Explorador de servidores/Explorador de bases de datos, actualice el mismo registro que actualizó en el formulario, pero cámbielo a un valor diferente y, a continuación, confirme el cambio.
Regrese al formulario y haga clic en Sincronizar ahora.
Compruebe la actualización en la cuadrícula de la aplicación y la base de datos servidor. Observe que la actualización que realizó en el cliente ha sobrescrito la actualización en el servidor.
Quitar las columnas de seguimiento del servidor de los comandos de sincronización
Cuando se crea la memoria caché de base de datos local, las columnas que se utilizan para realizar el seguimiento de los cambios en la base de datos servidor se descargan en el cliente. (En este tutorial, las columnas son CreationDate y LastEditDate). Para admitir la sincronización bidireccional y ayudar a garantizar la convergencia de datos en el cliente y el servidor, quite estas columnas de los comandos SQL que aplican cambios en la base de datos servidor. También puede quitar las columnas de los comandos que seleccionan la aplicación en el cliente de los cambios del servidor, aunque no es necesario. Debido a las restricciones en algunos cambios de esquema en la base de datos cliente, las columnas no se pueden colocar. Para obtener más información sobre los comandos de sincronización, vea How to: Specify Snapshot, Download, Upload, and Bidirectional Synchronization.
Nota: |
---|
Si utiliza el seguimiento de cambios de SQL Server 2008, en las tablas no se agregan las columnas de seguimiento. En este caso, no tiene que cambiar los comandos que aplican cambios en el servidor. |
El código siguiente vuelve a definir dos comandos que se establecen como propiedades en el objeto SyncAdapter de la tabla Customers: las propiedades InsertCommand() y UpdateCommand(). Los comandos que generó el cuadro de diálogo Configurar sincronización de datos contenían referencias a las columnas CreationDate y LastEditDate. En el código siguiente, estos comandos se vuelven a definir en el método OnInitialized de la clase CustomersSyncAdapter. La propiedad DeleteCommand() no se vuelve a definir porque no afecta a las columnas CreationDate ni LastEditDate.
Las variables de cada comando SQL se utilizan para pasar datos y metadatos entre Servicios de sincronización, el cliente y el servidor. En los comandos siguientes se utilizan estas variables de sesión:
@sync\_row\_count: devuelve el número de filas a las que afectó la última operación en el servidor. En bases de datos de SQL Server, @@rowcount proporciona el valor para esta variable.
@sync\_force\_write: se utiliza para forzar la aplicación de un cambio en el que se produjo un error debido a un conflicto o un error.
@sync\_last\_received\_anchor: se utiliza para definir el conjunto de cambios que se van a sincronizar durante una sesión.
Para obtener más información sobre las variables de sesión, vea How to: Use Session Variables.
Para quitar las columnas de seguimiento de los comandos de sincronización
Agregue el siguiente código a la clase NorthwindCache (NorthwindCache.vb o NorthwindCache.cs) después de la instrucción End Class de la clase NorthwindCacheServerSyncProvider.
public partial class CustomersSyncAdapter { partial void OnInitialized() { // Redefine the insert command so that it does not insert values // into the CreationDate and LastEditDate columns. System.Data.SqlClient.SqlCommand insertCommand = new _ System.Data.SqlClient.SqlCommand(); insertCommand.CommandText = "INSERT INTO dbo.Customers ([CustomerID], [CompanyName], " + "[ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], " + "[Country], [Phone], [Fax] )" + "VALUES (@CustomerID, @CompanyName, @ContactName, @ContactTitle, @Address, @City, " + "@Region, @PostalCode, @Country, @Phone, @Fax) SET @sync_row_count = @@rowcount"; insertCommand.CommandType = System.Data.CommandType.Text; insertCommand.Parameters.Add("@CustomerID", System.Data.SqlDbType.NChar); insertCommand.Parameters.Add("@CompanyName", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@ContactName", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@ContactTitle", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@Address", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@City", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@Region", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@PostalCode", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@Country", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@Phone", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@Fax", System.Data.SqlDbType.NVarChar); insertCommand.Parameters.Add("@sync_row_count", System.Data.SqlDbType.Int); insertCommand.Parameters["@sync_row_count"].Direction = System.Data.ParameterDirection.Output; this.InsertCommand = insertCommand; // Redefine the update command so that it does not update values // in the CreationDate and LastEditDate columns. System.Data.SqlClient.SqlCommand updateCommand = new System.Data.SqlClient.SqlCommand(); updateCommand.CommandText = "UPDATE dbo.Customers SET [CompanyName] = @CompanyName, [ContactName] " + "= @ContactName, [ContactTitle] = @ContactTitle, [Address] = @Address, [City] " + "= @City, [Region] = @Region, [PostalCode] = @PostalCode, [Country] = @Country, " + "[Phone] = @Phone, [Fax] = @Fax " + "WHERE ([CustomerID] = @CustomerID) AND (@sync_force_write = 1 " + "OR ([LastEditDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@rowcount"; updateCommand.CommandType = System.Data.CommandType.Text; updateCommand.Parameters.Add("@CompanyName", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@ContactName", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@ContactTitle", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@Address", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@City", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@Region", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@PostalCode", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@Country", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@Phone", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@Fax", System.Data.SqlDbType.NVarChar); updateCommand.Parameters.Add("@CustomerID", System.Data.SqlDbType.NChar); updateCommand.Parameters.Add("@sync_force_write", System.Data.SqlDbType.Bit); updateCommand.Parameters.Add("@sync_last_received_anchor", System.Data.SqlDbType.DateTime); updateCommand.Parameters.Add("@sync_row_count", System.Data.SqlDbType.Int); updateCommand.Parameters["@sync_row_count"].Direction = System.Data.ParameterDirection.Output; this.UpdateCommand = updateCommand; } }
Partial Public Class CustomersSyncAdapter Private Sub OnInitialized() ' Redefine the insert command so that it does not insert values ' into the CreationDate and LastEditDate columns. Dim insertCommand As New System.Data.SqlClient.SqlCommand With insertCommand .CommandText = "INSERT INTO dbo.Customers ([CustomerID], [CompanyName], " & _ "[ContactName], [ContactTitle], [Address], [City], [Region], [PostalCode], " & _ "[Country], [Phone], [Fax] )" & _ "VALUES (@CustomerID, @CompanyName, @ContactName, @ContactTitle, @Address, @City, " & _ "@Region, @PostalCode, @Country, @Phone, @Fax) SET @sync_row_count = @@rowcount" .CommandType = System.Data.CommandType.Text .Parameters.Add("@CustomerID", System.Data.SqlDbType.NChar) .Parameters.Add("@CompanyName", System.Data.SqlDbType.NVarChar) .Parameters.Add("@ContactName", System.Data.SqlDbType.NVarChar) .Parameters.Add("@ContactTitle", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Address", System.Data.SqlDbType.NVarChar) .Parameters.Add("@City", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Region", System.Data.SqlDbType.NVarChar) .Parameters.Add("@PostalCode", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Country", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Phone", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Fax", System.Data.SqlDbType.NVarChar) .Parameters.Add("@sync_row_count", System.Data.SqlDbType.Int) .Parameters("@sync_row_count").Direction = ParameterDirection.Output End With Me.InsertCommand = insertCommand ' Redefine the update command so that it does not update values ' in the CreationDate and LastEditDate columns. Dim updateCommand As New System.Data.SqlClient.SqlCommand With updateCommand .CommandText = "UPDATE dbo.Customers SET [CompanyName] = @CompanyName, [ContactName] " & _ "= @ContactName, [ContactTitle] = @ContactTitle, [Address] = @Address, [City] " & _ "= @City, [Region] = @Region, [PostalCode] = @PostalCode, [Country] = @Country, " & _ "[Phone] = @Phone, [Fax] = @Fax " & _ "WHERE ([CustomerID] = @CustomerID) AND (@sync_force_write = 1 " & _ "OR ([LastEditDate] <= @sync_last_received_anchor)) SET @sync_row_count = @@rowcount" .CommandType = System.Data.CommandType.Text .Parameters.Add("@CompanyName", System.Data.SqlDbType.NVarChar) .Parameters.Add("@ContactName", System.Data.SqlDbType.NVarChar) .Parameters.Add("@ContactTitle", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Address", System.Data.SqlDbType.NVarChar) .Parameters.Add("@City", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Region", System.Data.SqlDbType.NVarChar) .Parameters.Add("@PostalCode", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Country", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Phone", System.Data.SqlDbType.NVarChar) .Parameters.Add("@Fax", System.Data.SqlDbType.NVarChar) .Parameters.Add("@CustomerID", System.Data.SqlDbType.NChar) .Parameters.Add("@sync_force_write", System.Data.SqlDbType.Bit) .Parameters.Add("@sync_last_received_anchor", System.Data.SqlDbType.DateTime) .Parameters.Add("@sync_row_count", System.Data.SqlDbType.Int) .Parameters("@sync_row_count").Direction = ParameterDirection.Output End With Me.UpdateCommand = updateCommand End Sub End Class
Probar la aplicación
Para sincronizar y ver una actualización de columnas de seguimiento
Presione F5.
En el formulario, cambie un valor de la columna LastEditDate para actualizar un registro y, a continuación, haga clic en el botón Guardar.
Regrese al formulario y haga clic en Sincronizar ahora.
Compruebe la actualización en la cuadrícula de la aplicación y la base de datos servidor. Observe que el valor de columna del servidor ha sobrescrito la actualización en el cliente. El proceso de actualización se realiza como sigue:
Servicios de sincronización determina que ha cambiado una fila en el cliente.
Durante la sincronización, la fila se carga y se aplica a la tabla de la base de datos servidor. Sin embargo, las columnas de seguimiento no están incluidas en la instrucción de actualización. Servicios de sincronización realiza eficazmente una "actualización ficticia" en la tabla.
Ahora la fila se devuelve al cliente; los comandos que seleccionan los cambios del servidor sí incluyen las columnas de seguimiento. Por tanto, el cambio realizado en el cliente se sobrescribe con el valor del servidor.
Pasos siguientes
En este tutorial ha configurado la sincronización bidireccional con control básico de conflictos y ha resuelto el problema potencial de contar con columnas de seguimiento del servidor en la base de datos cliente. Mediante la utilización de clases parciales, puede extender el código de la memoria caché de base de datos local de otras maneras significativas. Por ejemplo, puede volver a definir los comandos SQL que seleccionan cambios de la base de datos servidor para que los datos se filtren al descargarse en el cliente. Se recomienda leer los temas "Cómo..." de esta documentación para entender las formas en que se puede agregar o cambiar el código de sincronización para satisfacer las necesidades de las aplicaciones. Para obtener más información, vea How to Program Common Client and Server Synchronization Tasks.
Vea también
Conceptos
Información general sobre las aplicaciones conectadas ocasionalmente
Otros recursos
How to Program Common Client and Server Synchronization Tasks
Tools to Help You Develop Applications (Synchronization Services)
Historial de cambios
Fecha |
Historial |
Motivo |
---|---|---|
Julio de 2008 |
Se ha agregado un tema. |
Cambio de características de SP1. |