Configurar y ejecutar la sincronización de colaboración (SQL Server)
En este tema se describen los componentes clave de una aplicación que utiliza Sync Framework para sincronizar bases de datos de SQL Server y SQL Server Compact. El código de esta aplicación se centra en las clases de Sync Framework siguientes:
Para obtener más información acerca de cómo ejecutar código de ejemplo, vea "Aplicaciones de ejemplo en los temas sobre procedimientos" en Sincronizar SQL Server y SQL Server Compact.
Tal y como se describe en Arquitectura y clases de la sincronización de colaboración, la sincronización puede producirse entre dos instancias de SqlSyncProvider, dos instancias de SqlCeSyncProvider o una instancia de cada proveedor. El código de ejemplo de este tema procede de una aplicación de dos niveles; por tanto, aquí no se muestra la sincronización de dos instancias de SqlCeSyncProvider, lo que requiere una configuración de n niveles. Para obtener un ejemplo de una configuración de n niveles, vea el ejemplo de WebSharingAppDemo-CEProviderEndToEnd que se incluye en el SDK de Sync Framework.
Comparar tipos de proveedor
En este tema se describe cómo se sincronizan las bases de datos de SQL Server y SQL Server Compact utilizando los dos proveedores de sincronización que se introdujeron en Sync Framework 2.0: SqlSyncProvider y SqlCeSyncProvider. Sync Framework contiene otros proveedores que pueden sincronizar estas bases de datos pero, en general, los nuevos proveedores son más adecuados para esta tarea por las razones siguientes:
SqlSyncProvider tiene la misma capacidad que DbSyncProvider, pero necesita mucho menos código y menos conocimiento de las consultas que Sync Framework utiliza para sincronizar los datos. DbSyncProvider sigue siendo adecuado para las bases de datos que no utilizan SQL Server.
SqlSyncProvider y SqlCeSyncProvider se pueden utilizar en topologías cliente-servidor, topologías punto a punto y topologías mixtas, mientras que DbServerSyncProvider y SqlCeClientSyncProvider solamente son adecuados para topologías cliente-servidor. SqlSyncProvider y SqlCeSyncProvider también admiten características más avanzadas, como el procesamiento por lotes de los cambios en función del tamaño de los datos en lugar del número de filas.
Los proveedores SqlSyncProvider y SqlCeSyncProvider son flexibles y fáciles de configurar. Permiten sincronizar todas las ediciones de SQL Server, incluidas las versiones SQL Server Express y SQL Server Compact.
Configurar los nodos y ejecutar la sincronización
La sincronización de los nodos de una topología puede estructurarse en dos fases: la configuración de los nodos que se van a sincronizar y la ejecución real de la sincronización entre un par de nodos. En SqlSyncProvider y SqlCeSyncProvider, la configuración de los nodos se compone de dos tareas:
La definición de lo que se va a sincronizar
Para definir lo que va a sincronizar, describa uno o varios ámbitos. Un ámbito es un conjunto de tablas, donde algunas o todas se pueden filtrar. Las tablas pueden existir previamente en la base de datos o pueden describirse a través de objetos de Sync Framework y, a continuación, generarse en tiempo de ejecución cuando el almacén subyacente se esté sincronizando. Para obtener más información, vea la sección "Descripción de los ámbitos", más adelante en este tema.
Importante
Una vez que un ámbito se sincroniza por primera vez, deben quitarse los metadatos asociados a los ámbitos existentes y crearse de nuevo si fuera necesario cambiar algún ámbito.
El aprovisionamiento de bases de datos para el seguimiento de cambios de Sync Framework
Una vez que se han descrito las tablas y los ámbitos, hay que utilizar objetos de Sync Framework para aplicar los scripts de aprovisionamiento a cada nodo. Los scripts crean una infraestructura de seguimiento y aplicación de cambios que se compone de metadatos, tablas, desencadenadores y procedimientos almacenados.
Una vez que se han aprovisionado los nodos, se pueden sincronizar. Desde el punto de vista del desarrollador, la configuración de las opciones de sincronización y la llamada a Synchronize()
resulta sencilla. En segundo plano, Sync Framework utiliza la información de descripción de las tablas y ámbitos que se especificó al crear un objeto de configuración para cada ámbito y cada adaptador de sincronización (uno por cada tabla). De este modo, Sync Framework puede tomar la información que se mantiene en cada base de datos y generar la información que necesita en cada sesión de sincronización entre un par de nodos. SqlSyncProvider y SqlCeSyncProvider tienen constancia de las tablas de seguimiento de cambios y demás objetos que se crearon durante el aprovisionamiento, y generan automáticamente los objetos DbSyncAdapter necesarios. De este modo, se reduce considerablemente el código necesario para sincronizar los datos utilizando estos proveedores.
En la tabla siguiente se enumeran las clases que se utilizan para configurar las bases de datos y los proveedores.
SQL Server | SQL Server Compact | Descripción |
---|---|---|
DbSyncScopeDescription |
DbSyncScopeDescription |
Representa un ámbito de sincronización, que es una agrupación lógica de tablas que se sincronizan como una unidad. |
SqlSyncScopeProvisioning |
Representa el aprovisionamiento de una base de datos de SQL Server o SQL Server Compact para un ámbito determinado que se representa mediante un objeto DbSyncScopeDescription. |
|
Representa información de configuración que SqlSyncProvider o SqlCeSyncProvider utilizan para un ámbito determinado. |
||
DbSyncTableDescription |
DbSyncTableDescription |
Representa el esquema de una tabla que está incluida en un ámbito de sincronización. |
DbSyncColumnDescription |
Representa las propiedades de una columna que forma parte de una tabla que está incluida en un ámbito de sincronización. |
|
SqlSyncDescriptionBuilder |
SqlCeSyncDescriptionBuilder |
Representa información sobre las tablas y ámbitos de una base de datos de SQL Server o SQL Server Compact que está implicada en sincronización. Se utiliza para extraer los objetos |
SqlSyncTableProvisioning |
Representa el aprovisionamiento de una tabla de base de datos de SQL Server o SQL Server Compact (con filtros opcionales) que se representa mediante un objeto DbSyncTableDescription. |
|
SqlSyncProviderAdapterConfiguration |
Representa información de configuración del adaptador de sincronización de una tabla incluida en una base de datos de SQL Server o SQL Server Compact. |
Además de estos tipos principales, hay otros cuatro tipos importantes que deben tenerse en cuenta:
SqlSyncStoreMetadataCleanup y SqlCeSyncStoreMetadataCleanup, que permiten limpiar los metadatos antiguos de las tablas de seguimiento de cambios de Sync Framework. Para obtener más información, vea Limpiar los metadatos de la sincronización de colaboración (SQL Server).
SqlCeSyncStoreSnapshotInitialization, que permite generar una instantánea de un archivo de base de datos de SQL Server Compact existente que puede utilizarse para inicializar otra base de datos de SQL Server Compact para la sincronización. Para obtener más información, vea Aprovisionar los clientes en este tema.
SqlSyncStoreRestore, que permite actualizar los metadatos de seguimiento de cambios después de que una base de datos de SQL Server se restaura a partir de una copia de seguridad, lo que es necesario para que una base de datos restaurada pueda sincronizarse de nuevo. Para obtener más información, vea Realizar copias de seguridad y restauración de una base de datos (SQL Server).
Descripción de los ámbitos
Nota
Esta sección del tema contiene información adicional sobre los ámbitos de sincronización. Si lo desea, puede avanzar hasta la sección "Ejemplos de código", pero sería conveniente que leyera esta sección si planea utilizar ámbitos filtrados o varios ámbitos en una aplicación.
Es importante entender que un ámbito es la combinación de tablas y filtros. Por ejemplo, puede definir un ámbito filtrado denominado sales-WA
que contenga únicamente los datos de ventas del estado de Washington de la tabla customer_sales
. Si define otro filtro de la misma tabla, como sales-OR
, se tratará de un ámbito diferente. Si define filtros, tenga en cuenta que Sync Framework no administra automáticamente la eliminación de filas que ya no satisfacen una condición del filtro. Por ejemplo, si un usuario o una aplicación actualiza un valor en una columna que se utiliza para filtrar, se mueve una fila de un ámbito a otro. La fila se envía al nuevo ámbito al que ahora pertenece, pero no se elimina del ámbito anterior. Su aplicación debe controlar esta situación.
Los ámbitos pueden ser distintos o pueden superponerse entre sí. Dos ámbitos se superponen si comparten datos comunes entre ellos. Por ejemplo, la tabla products
podría estar incluida en un ámbito sales
y un ámbito inventory
. Estos dos ámbitos pueden estar superpuestos y filtrados. En los escenarios siguientes se muestran los modos en que pueden producirse las operaciones de filtrado y solapamiento:
Escenario 1:
El ámbito 1 es
sales-WA
. Este ámbito incluye:products
;orders
, con un filtro destate=WA
, yorder_details
, con un filtro destate=WA
.El ámbito 2 es
sales-OR
. Este ámbito incluye:products
;orders
, con un filtro destate=OR
, yorder_details
, con un filtro destate=OR
.
En este escenario, los dos ámbitos comparten la tabla
products
completa. Las tablasorders
yorder_details
están en los dos ámbitos, pero los filtros no se superponen; por tanto, los ámbitos no comparten filas de estas tablas.Escenario 2:
El ámbito 1 es
sales-WA
. Este ámbito incluye:products
;orders
, con un filtro destate=WA
, yorder_details
, con un filtro destate=WA
.El ámbito 2 es
sales-Northwest
. Este ámbito incluye:products
;orders
, con un filtro destate=WA OR state=ID
, yshippers
.
En este escenario, los dos ámbitos comparten de nuevo la tabla
products
completa. La tablaorders
está en los dos ámbitos y los filtros se superponen; los dos ámbitos comparten, por tanto, las filas que satisfacen el filtrostate=WA
. Las tablasshippers
yorder_details
no se comparten entre los ámbitos.
Hay muchas maneras diferentes en las que se pueden definir los ámbitos, pero debe seguirse este principio: todos los datos que se sincronicen entre un par de bases de datos en la topología de sincronización pueden pertenecer exclusivamente a un único ámbito. Por ejemplo, en el escenario 2 anterior, la base de datos A y la base de datos B podrían sincronizar el ámbito 1, mientras que la base de datos A y la base de datos C podrían sincronizar el ámbito 2. La base de datos A y la base de datos B no podrían sincronizar el ámbito 2 porque las filas de products
y orders
pertenecen a los dos ámbitos.
Ejemplos de código
Los ejemplos de código de esta sección contienen muchos de los objetos descritos anteriormente y en ellos se tratan las tareas siguientes:
Describir el ámbito y las tablas
Aprovisionar el servidor
Aprovisionar los clientes
Establecer opciones de sincronización
Sincronizar los nodos
Después de tratar cada una de estas áreas, se incluye una aplicación de consola completa que combina cada uno de estos ejemplos y otro código adicional para sincronizar una topología de cuatro nodos. La topología está compuesta de un servidor de SQL Server, un cliente de SQL Server y dos clientes de SQL Server Compact.
Describir el ámbito y las tablas
En el ejemplo de código siguiente se describe un ámbito denominado filtered_customer
y se agregan dos tablas al mismo: Customer
y CustomerContact
. Las tablas ya existen en la base de datos servidor, de modo que el método GetDescriptionForTable se utiliza para recuperar el esquema de la base de datos servidor. Se incluyen todas las columnas de la tabla Customer
, pero solo se incluyen dos columnas de la tabla CustomerContact
.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("filtered_customer");
// Definition for Customer.
DbSyncTableDescription customerDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
scopeDesc.Tables.Add(customerDescription);
// Definition for CustomerContact, including the list of columns to include.
Collection<string> columnsToInclude = new Collection<string>();
columnsToInclude.Add("CustomerId");
columnsToInclude.Add("PhoneType");
DbSyncTableDescription customerContactDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn);
scopeDesc.Tables.Add(customerContactDescription);
Dim scopeDesc As New DbSyncScopeDescription("filtered_customer")
' Definition for Customer.
Dim customerDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
scopeDesc.Tables.Add(customerDescription)
' Definition for CustomerContact, including the list of columns to include.
Dim columnsToInclude As New Collection(Of String)()
columnsToInclude.Add("CustomerId")
columnsToInclude.Add("PhoneType")
Dim customerContactDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn)
scopeDesc.Tables.Add(customerContactDescription)
Aprovisionar el servidor
En el ejemplo de código siguiente se crea un objeto de aprovisionamiento para el ámbito filtered_customer
, se especifica que las tablas base no deben crearse en la base de datos servidor, y se especifica que todos los objetos relacionados con la sincronización deben crearse en un esquema de la base de datos denominado "Sync"
. Durante el aprovisionamiento del ámbito, el código define un filtro de la tabla Customer
. Solo se van a sincronizar las filas que satisfagan ese filtro. En la tabla CustomerContact
no hay definido ningún filtro; por tanto, se sincronizarán todas las filas de esa tabla. Una vez definidas las opciones de aprovisionamiento, se llama al método Apply para crear la infraestructura de seguimiento de cambios de la base de datos servidor; a continuación, el script de aprovisionamiento se escribe en un archivo.
SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(scopeDesc);
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
serverConfig.ObjectSchema = "Sync";
// Specify which column(s) in the Customer table to use for filtering data,
// and the filtering clause to use against the tracking table.
// "[side]" is an alias for the tracking table.
serverConfig.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverConfig.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = 'Retail'";
// Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn);
// Write the configuration script to a file. You can modify
// this script if necessary and run it against the server
// to customize behavior.
File.WriteAllText("SampleConfigScript.txt",
serverConfig.Script("SyncSamplesDb_SqlPeer1"));
Dim serverConfig As New SqlSyncScopeProvisioning(scopeDesc)
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
serverConfig.ObjectSchema = "Sync"
' Specify which column(s) in the Customer table to use for filtering data,
' and the filtering clause to use against the tracking table.
' "[side]" is an alias for the tracking table.
serverConfig.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverConfig.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = 'Retail'"
' Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn)
' Write the configuration script to a file. You can modify
' this script if necessary and run it against the server
' to customize behavior.
File.WriteAllText("SampleConfigScript.txt", serverConfig.Script("SyncSamplesDb_SqlPeer1"))
Aprovisionar los clientes
En esta aplicación, los clientes se aprovisionan de dos maneras diferentes:
La inicialización completa de una base de datos cliente de SQL Server o SQL Server Compact basándose en la información de ámbito que se recupera del servidor o de otra base de datos cliente.
Los objetos de usuario y los objetos de sincronización se crean en la base de datos cliente basándose en la información de esquema que los objetos SqlCeSyncDescriptionBuilder y SqlSyncDescriptionBuilder ponen disponible. Durante la primera sesión de sincronización, la base de datos cliente se prepara para la sincronización y todas las filas se descargan a la base de datos cliente como inserciones incrementales.
La inicialización de la instantánea de una base de datos cliente de SQL Server Compact mediante una base de datos cliente que ya existe.
La inicialización de una instantánea está diseñada para reducir el tiempo necesario para inicializar una base de datos cliente. Una vez que se ha inicializado una base de datos cliente a través de la inicialización completa, se pueden inicializar posteriormente otras bases de datos utilizando una instantánea de esta primera base de datos cliente. Una instantánea es una base de datos de SQL Server Compact preparada de manera especial que contiene el esquema de tabla, los datos (opcional) y la infraestructura de seguimiento de cambios. Copie esta instantánea en cada cliente que lo necesite. Durante la primera sesión de sincronización de un cliente, los metadatos específicos del cliente se actualizan y se descarga en la base de datos cliente cualquier cambio que se haya producido desde que se creó la instantánea.
Importante
Las instantáneas deberían generarse únicamente cuando no haya actividad en la base de datos de SQL Server Compact. No se admiten operaciones simultáneas de ningún tipo durante la generación de la instantánea.
En el ejemplo de código siguiente primero se recupera información del ámbito del servidor y se utilizan los esquemas del seguimiento de cambios y la tabla base que se recuperaron para aprovisionar una base de datos cliente de SQL Server Compact. A continuación, el código aprovisiona una base de datos cliente de SQL Server basándose en la información del ámbito de la base de datos cliente de SQL Server Compact.
// Create a SQL Server Compact database and provision it based on scope
// information that is retrieved from the server. Compact databases
// do not support separate schemas, so we prefix the name of all
// synchronization-related objects with "Sync" so that they are easy to
// identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, false);
DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", null, "Sync", serverConn);
SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Desc);
clientSqlCe1Config.ObjectPrefix = "Sync";
clientSqlCe1Config.Apply(clientSqlCe1Conn);
// Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
// information that is retrieved from the SQL Server Compact database. We could
// have also retrieved this information from the server.
DbSyncScopeDescription clientSqlDesc = SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply(clientSqlConn);
' Create a SQL Server Compact database and provision it based on scope
' information that is retrieved from the server. Compact databases
' do not support separate schemas, so we prefix the name of all
' synchronization-related objects with "Sync" so that they are easy to
' identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, False)
Dim clientSqlCe1Desc As DbSyncScopeDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", Nothing, "Sync", serverConn)
Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Desc)
clientSqlCe1Config.ObjectPrefix = "Sync"
clientSqlCe1Config.Apply(clientSqlCe1Conn)
' Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
' information that is retrieved from the SQL Server Compact database. We could
' have also retrieved this information from the server.
Dim clientSqlDesc As DbSyncScopeDescription = _
SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply(clientSqlConn)
En el ejemplo de código siguiente se genera una instantánea denominada SyncSampleClient2.sdf
de la base de datos SyncSampleClient1.sdf
. A continuación, el código sincroniza SyncSampleClient2.sdf
con la base de datos servidor.
// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");
// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
' Create a snapshot from the SQL Server Compact database, which will be used to
' initialize a second Compact database. Again, this database could be provisioned
' by retrieving scope information from another database, but we want to
' demonstrate the use of snapshots, which provide a convenient deployment
' mechanism for Compact databases.
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")
' The new SQL Server Compact client synchronizes with the server, but
' no data is downloaded because the snapshot already contains
' all of the data from the first Compact database.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
Establecer opciones de sincronización
El ejemplo de código siguiente es un constructor de la clase SampleSyncOrchestrator
de la aplicación. El constructor toma dos objetos RelationalSyncProvider
porque tanto SqlSyncProvider
como SqlCeSyncProvider
se derivan de RelationalSyncProvider
. El código especifica cuál es el proveedor local y cuál el proveedor remoto. A continuación, especifica qué cambios se cargan primero desde la base de datos remota a la base de datos local y posteriormente los descarga en la otra dirección.
public SampleSyncOrchestrator(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
{
this.LocalProvider = localProvider;
this.RemoteProvider = remoteProvider;
this.Direction = SyncDirectionOrder.UploadAndDownload;
}
Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)
Me.LocalProvider = localProvider
Me.RemoteProvider = remoteProvider
Me.Direction = SyncDirectionOrder.UploadAndDownload
End Sub
Sincronizar los nodos
En el ejemplo de código siguiente se crean instancias de los proveedores en tres sesiones de sincronización diferentes: la primera entre el servidor y el cliente de SQL Server, la segunda entre el cliente de SQL Server y uno de los clientes de SQL Server Compact, y la tercera entre el servidor y el otro cliente de SQL Server Compact. Durante las dos primeras sesiones se sincronizan siete filas: las cuatro filas de CustomerContact
y las tres filas de Customer
que satisfacen los criterios del filtrado. En la tercera sesión no se sincronizan filas porque la instantánea ya contiene todos los datos de la primera base de datos de SQL Server Compact. En la aplicación se muestran las estadísticas que devuelve el método Synchronize()
.
SampleSyncOrchestrator syncOrchestrator;
SyncOperationStatistics syncStats;
// Data is downloaded from the server to the SQL Server client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Data is downloaded from the SQL Server client to the
// first SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");
// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
Dim syncOrchestrator As SampleSyncOrchestrator
Dim syncStats As SyncOperationStatistics
' Data is downloaded from the server to the SQL Server client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Data is downloaded from the SQL Server client to the
' first SQL Server Compact client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Create a snapshot from the SQL Server Compact database, which will be used to
' initialize a second Compact database. Again, this database could be provisioned
' by retrieving scope information from another database, but we want to
' demonstrate the use of snapshots, which provide a convenient deployment
' mechanism for Compact databases.
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")
' The new SQL Server Compact client synchronizes with the server, but
' no data is downloaded because the snapshot already contains
' all of the data from the first Compact database.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
Ejemplo de código completo
En el ejemplo de código completo siguiente se incluyen los ejemplos de código descritos anteriormente en este tema y código adicional para mostrar las estadísticas de sincronización y la información de eventos. Para el ejemplo, se requiere que la clase Utility
esté disponible en Clase de utilidad para los temas de procedimientos del proveedor de bases de datos.
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlServerCe;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
using Microsoft.Synchronization.Data.SqlServerCe;
namespace Microsoft.Samples.Synchronization
{
class Program
{
static void Main(string[] args)
{
// Create the connections over which provisioning and synchronization
// are performed. The Utility class handles all functionality that is not
// directly related to synchronization, such as holding connection
// string information and making changes to the server database.
SqlConnection serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
SqlConnection clientSqlConn = new SqlConnection(Utility.ConnStr_SqlSync_Client);
SqlCeConnection clientSqlCe1Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync1);
SqlCeConnection clientSqlCe2Conn = new SqlCeConnection(Utility.ConnStr_SqlCeSync2);
// Create a scope named "filtered_customer", and add two tables to the scope.
// GetDescriptionForTable gets the schema of each table, so that tracking
// tables and triggers can be created for that table. For Customer, we add
// the entire table. For CustomerContact, we add only two of the columns.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("filtered_customer");
// Definition for Customer.
DbSyncTableDescription customerDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);
scopeDesc.Tables.Add(customerDescription);
// Definition for CustomerContact, including the list of columns to include.
Collection<string> columnsToInclude = new Collection<string>();
columnsToInclude.Add("CustomerId");
columnsToInclude.Add("PhoneType");
DbSyncTableDescription customerContactDescription =
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn);
scopeDesc.Tables.Add(customerContactDescription);
// Create a provisioning object for "filtered_customer". We specify that
// base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1),
// and that all synchronization-related objects should be created in a
// database schema named "Sync". If you specify a schema, it must already exist
// in the database.
SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(scopeDesc);
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
serverConfig.ObjectSchema = "Sync";
// Specify which column(s) in the Customer table to use for filtering data,
// and the filtering clause to use against the tracking table.
// "[side]" is an alias for the tracking table.
serverConfig.Tables["Sales.Customer"].AddFilterColumn("CustomerType");
serverConfig.Tables["Sales.Customer"].FilterClause = "[side].[CustomerType] = 'Retail'";
// Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn);
// Write the configuration script to a file. You can modify
// this script if necessary and run it against the server
// to customize behavior.
File.WriteAllText("SampleConfigScript.txt",
serverConfig.Script("SyncSamplesDb_SqlPeer1"));
// Provision each of the client databases.
// Create a SQL Server Compact database and provision it based on scope
// information that is retrieved from the server. Compact databases
// do not support separate schemas, so we prefix the name of all
// synchronization-related objects with "Sync" so that they are easy to
// identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, false);
DbSyncScopeDescription clientSqlCe1Desc = SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", null, "Sync", serverConn);
SqlCeSyncScopeProvisioning clientSqlCe1Config = new SqlCeSyncScopeProvisioning(clientSqlCe1Desc);
clientSqlCe1Config.ObjectPrefix = "Sync";
clientSqlCe1Config.Apply(clientSqlCe1Conn);
// Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
// information that is retrieved from the SQL Server Compact database. We could
// have also retrieved this information from the server.
DbSyncScopeDescription clientSqlDesc = SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlDesc);
clientSqlConfig.ObjectSchema = "Sync";
clientSqlConfig.Apply(clientSqlConn);
// Initial synchronization sessions. 7 rows are synchronized:
// all rows (4) from CustomerContact, and the 3 rows from Customer
// that satisfy the filtering criteria.
SampleSyncOrchestrator syncOrchestrator;
SyncOperationStatistics syncStats;
// Data is downloaded from the server to the SQL Server client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Data is downloaded from the SQL Server client to the
// first SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Create a snapshot from the SQL Server Compact database, which will be used to
// initialize a second Compact database. Again, this database could be provisioned
// by retrieving scope information from another database, but we want to
// demonstrate the use of snapshots, which provide a convenient deployment
// mechanism for Compact databases.
SqlCeSyncStoreSnapshotInitialization syncStoreSnapshot = new SqlCeSyncStoreSnapshotInitialization("Sync");
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf");
// The new SQL Server Compact client synchronizes with the server, but
// no data is downloaded because the snapshot already contains
// all of the data from the first Compact database.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer");
// Synchronize again. Three changes were made on the server, but
// only two of them applied to rows that are in the "filtered_customer"
// scope. The other row is not synchronized.
// Notice that the order of synchronization is different from the initial
// sessions, but the two changes are propagated to all nodes.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"),
new SqlSyncProvider("filtered_customer", serverConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync"),
new SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"),
new SqlSyncProvider("filtered_customer", clientSqlConn, null, "Sync")
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
serverConn.Close();
serverConn.Dispose();
clientSqlConn.Close();
clientSqlConn.Dispose();
clientSqlCe1Conn.Close();
clientSqlCe1Conn.Dispose();
clientSqlCe2Conn.Close();
clientSqlCe2Conn.Dispose();
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server);
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client);
Console.Write("\nPress any key to exit.");
Console.Read();
}
}
public class SampleSyncOrchestrator : SyncOrchestrator
{
public SampleSyncOrchestrator(RelationalSyncProvider localProvider, RelationalSyncProvider remoteProvider)
{
this.LocalProvider = localProvider;
this.RemoteProvider = remoteProvider;
this.Direction = SyncDirectionOrder.UploadAndDownload;
}
public void DisplayStats(SyncOperationStatistics syncStatistics, string syncType)
{
Console.WriteLine(String.Empty);
if (syncType == "initial")
{
Console.WriteLine("****** Initial Synchronization ******");
}
else if (syncType == "subsequent")
{
Console.WriteLine("***** Subsequent Synchronization ****");
}
Console.WriteLine("Start Time: " + syncStatistics.SyncStartTime);
Console.WriteLine("Total Changes Uploaded: " + syncStatistics.UploadChangesTotal);
Console.WriteLine("Total Changes Downloaded: " + syncStatistics.DownloadChangesTotal);
Console.WriteLine("Complete Time: " + syncStatistics.SyncEndTime);
Console.WriteLine(String.Empty);
}
}
}
Imports System
Imports System.Collections.ObjectModel
Imports System.IO
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient
Imports System.Data.SqlServerCe
Imports Microsoft.Synchronization
Imports Microsoft.Synchronization.Data
Imports Microsoft.Synchronization.Data.SqlServer
Imports Microsoft.Synchronization.Data.SqlServerCe
Namespace Microsoft.Samples.Synchronization
Class Program
Public Shared Sub Main(ByVal args As String())
' Create the connections over which provisioning and synchronization
' are performed. The Utility class handles all functionality that is not
' directly related to synchronization, such as holding connection
' string information and making changes to the server database.
Dim serverConn As New SqlConnection(Utility.ConnStr_SqlSync_Server)
Dim clientSqlConn As New SqlConnection(Utility.ConnStr_SqlSync_Client)
Dim clientSqlCe1Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync1)
Dim clientSqlCe2Conn As New SqlCeConnection(Utility.ConnStr_SqlCeSync2)
' Create a scope named "filtered_customer", and add two tables to the scope.
' GetDescriptionForTable gets the schema of each table, so that tracking
' tables and triggers can be created for that table. For Customer, we add
' the entire table. For CustomerContact, we add only two of the columns.
Dim scopeDesc As New DbSyncScopeDescription("filtered_customer")
' Definition for Customer.
Dim customerDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)
scopeDesc.Tables.Add(customerDescription)
' Definition for CustomerContact, including the list of columns to include.
Dim columnsToInclude As New Collection(Of String)()
columnsToInclude.Add("CustomerId")
columnsToInclude.Add("PhoneType")
Dim customerContactDescription As DbSyncTableDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", columnsToInclude, serverConn)
scopeDesc.Tables.Add(customerContactDescription)
' Create a provisioning object for "filtered_customer". We specify that
' base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1),
' and that all synchronization-related objects should be created in a
' database schema named "Sync". If you specify a schema, it must already exist
' in the database.
Dim serverConfig As New SqlSyncScopeProvisioning(scopeDesc)
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
serverConfig.ObjectSchema = "Sync"
' Specify which column(s) in the Customer table to use for filtering data,
' and the filtering clause to use against the tracking table.
' "[side]" is an alias for the tracking table.
serverConfig.Tables("Sales.Customer").AddFilterColumn("CustomerType")
serverConfig.Tables("Sales.Customer").FilterClause = "[side].[CustomerType] = 'Retail'"
' Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn)
' Write the configuration script to a file. You can modify
' this script if necessary and run it against the server
' to customize behavior.
File.WriteAllText("SampleConfigScript.txt", serverConfig.Script("SyncSamplesDb_SqlPeer1"))
' Provision each of the client databases.
' Create a SQL Server Compact database and provision it based on scope
' information that is retrieved from the server. Compact databases
' do not support separate schemas, so we prefix the name of all
' synchronization-related objects with "Sync" so that they are easy to
' identify.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync2, False)
Dim clientSqlCe1Desc As DbSyncScopeDescription = _
SqlSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", Nothing, "Sync", serverConn)
Dim clientSqlCe1Config As New SqlCeSyncScopeProvisioning(clientSqlCe1Desc)
clientSqlCe1Config.ObjectPrefix = "Sync"
clientSqlCe1Config.Apply(clientSqlCe1Conn)
' Provision the existing database SyncSamplesDb_SqlPeer2 based on scope
' information that is retrieved from the SQL Server Compact database. We could
' have also retrieved this information from the server.
Dim clientSqlDesc As DbSyncScopeDescription = _
SqlCeSyncDescriptionBuilder.GetDescriptionForScope("filtered_customer", "Sync", clientSqlCe1Conn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlDesc)
clientSqlConfig.ObjectSchema = "Sync"
clientSqlConfig.Apply(clientSqlConn)
' Initial synchronization sessions. 7 rows are synchronized:
' all rows (4) from CustomerContact, and the 3 rows from Customer
' that satisfy the filtering criteria.
Dim syncOrchestrator As SampleSyncOrchestrator
Dim syncStats As SyncOperationStatistics
' Data is downloaded from the server to the SQL Server client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Data is downloaded from the SQL Server client to the
' first SQL Server Compact client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Create a snapshot from the SQL Server Compact database, which will be used to
' initialize a second Compact database. Again, this database could be provisioned
' by retrieving scope information from another database, but we want to
' demonstrate the use of snapshots, which provide a convenient deployment
' mechanism for Compact databases.
Dim syncStoreSnapshot As New SqlCeSyncStoreSnapshotInitialization("Sync")
syncStoreSnapshot.GenerateSnapshot(clientSqlCe1Conn, "SyncSampleClient2.sdf")
' The new SQL Server Compact client synchronizes with the server, but
' no data is downloaded because the snapshot already contains
' all of the data from the first Compact database.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Make changes on the server: 1 insert, 1 update, and 1 delete.
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "Customer")
' Synchronize again. Three changes were made on the server, but
' only two of them applied to rows that are in the "filtered_customer"
' scope. The other row is not synchronized.
' Notice that the order of synchronization is different from the initial
' sessions, but the two changes are propagated to all nodes.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", serverConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"), _
New SqlCeSyncProvider("filtered_customer", clientSqlCe1Conn, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("filtered_customer", clientSqlCe2Conn, "Sync"), _
New SqlSyncProvider("filtered_customer", clientSqlConn, Nothing, "Sync"))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
serverConn.Close()
serverConn.Dispose()
clientSqlConn.Close()
clientSqlConn.Dispose()
clientSqlCe1Conn.Close()
clientSqlCe1Conn.Dispose()
clientSqlCe2Conn.Close()
clientSqlCe2Conn.Dispose()
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server)
Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client)
Console.Write(vbLf & "Press any key to exit.")
Console.Read()
End Sub
End Class
Public Class SampleSyncOrchestrator
Inherits SyncOrchestrator
Public Sub New(ByVal localProvider As RelationalSyncProvider, ByVal remoteProvider As RelationalSyncProvider)
Me.LocalProvider = localProvider
Me.RemoteProvider = remoteProvider
Me.Direction = SyncDirectionOrder.UploadAndDownload
End Sub
Public Sub DisplayStats(ByVal syncStatistics As SyncOperationStatistics, ByVal syncType As String)
Console.WriteLine([String].Empty)
If syncType = "initial" Then
Console.WriteLine("****** Initial Synchronization ******")
ElseIf syncType = "subsequent" Then
Console.WriteLine("***** Subsequent Synchronization ****")
End If
Console.WriteLine("Start Time: " & syncStatistics.SyncStartTime)
Console.WriteLine("Total Changes Uploaded: " & syncStatistics.UploadChangesTotal)
Console.WriteLine("Total Changes Downloaded: " & syncStatistics.DownloadChangesTotal)
Console.WriteLine("Complete Time: " & syncStatistics.SyncEndTime)
Console.WriteLine([String].Empty)
End Sub
End Class
End Namespace
Vea también
Conceptos
Información general sobre la sincronización de colaboración
Arquitectura y clases de la sincronización de colaboración