Procédure : nettoyer les métadonnées pour la synchronisation collaborative (SQL Server)
Cette rubrique décrit comment nettoyer des métadonnées pour les bases de données SQL Server et SQL Server Compact synchronisées à l'aide de Sync Framework. Le code proposé dans cette rubrique se concentre sur les classes Sync Framework suivantes :
Pour plus d'informations sur le mode d'exécution d'un exemple de code, consultez « Exemples d'application dans les rubriques de procédures » dans Synchronisation de SQL Server et SQL Server Compact.
Présentation du nettoyage des métadonnées
Le nettoyage implique la suppression des métadonnées des lignes qui ont été supprimées d'une table de base. Sync Framework utilise deux types de métadonnées :
Métadonnées de niveau table qui effectuent le suivi des insertions, mises à jour et suppressions pour chaque table qui est synchronisée.
Il existe une ligne de métadonnées pour chaque ligne dans la table de base. Si une ligne est supprimée de la table de base et que tous les nœuds de toutes les étendues l'ont reçue, la ligne de métadonnées peut être supprimée sans risque.
Métadonnées de niveau base de données qui effectuent le suivi des modifications reçues par chaque nœud d'autres nœuds.
En règle générale, ces métadonnées sont stockées dans une table d'étendue pour chaque base de données de nœud. Les lignes de la table d'étendue ne doivent jamais être supprimées, à moins que l'étendue ne soit elle aussi supprimée.
Le nettoyage est basé sur la rétention, ce qui signifie que les métadonnées qui sont plus anciennes que le nombre de jours spécifié sont supprimées. Pour les bases de données SQL Server, utilisez l'objet SqlSyncStoreMetadataCleanup et pour les bases de données SQL Server Compact, utilisez l'objet SqlCeSyncStoreMetadataCleanup. Les deux objets ont les mêmes propriétés et méthodes.
SQL Server | SQL Server Compact | Description |
---|---|---|
Méthode que vous appelez à partir de votre application pour nettoyer les métadonnées. |
||
Propriété qui spécifie l'ancienneté, en jours, que les métadonnées de suivi des modifications doivent atteindre avant d'être supprimées lorsque la méthode de nettoyage est appelée. |
Si un nœud essaie de synchroniser des modifications dont les métadonnées ont déjà été nettoyées, une exception de type DbOutdatedSyncException est levée. L'événement SyncPeerOutdated est déclenché, ce qui fournit l'accès à un objet DbOutdatedEventArgs. Il existe deux options pour gérer cet événement :
Affecter à la propriété Action la valeur PartialSync. Cette opération permet de synchroniser les données pour lesquelles des métadonnées sont présentes, mais certaines suppressions sont omises.
Affecter à la propriété Action la valeur AbortSync (qui est la valeur par défaut). Cette opération met fin à la session de synchronisation. Le client doit être réinitialisé dans la session de synchronisation suivante afin que ses données soient correctes.
Exemple de code complet
L'exemple de code complet effectue les étapes suivantes :
Synchronise SyncSamplesDb_SqlPeer1 (Node1) et SyncSamplesDb_SqlPeer1 (Node2). Neuf lignes sont téléchargées sur Node2.
Synchronise Node2 et SyncSampleClient1.sdf (Node3).
Effectue une insertion, une mise à jour et une suppression sur Node1.
Appelle PerformCleanup pour les métadonnées qui ont plus de 7 jours sur Node1. La méthode PerformCleanup est retournée avec succès, mais aucune métadonnée n'est nettoyée, car aucune suppression n'a été effectuée sur Node1 dans les 7 derniers jours.
SqlSyncStoreMetadataCleanup metadataCleanup = new SqlSyncStoreMetadataCleanup(serverConn); bool cleanupSuccessful; metadataCleanup.RetentionInDays = 7; cleanupSuccessful = metadataCleanup.PerformCleanup();
Dim metadataCleanup As New SqlSyncStoreMetadataCleanup(serverConn) Dim cleanupSuccessful As Boolean metadataCleanup.RetentionInDays = 7 cleanupSuccessful = metadataCleanup.PerformCleanup()
Synchronise Node1 et Node3, et Node2 et Node3. La synchronisation est réussie, car toutes les métadonnées pertinentes sont encore disponibles sur les deux nœuds.
Supprime une ligne de Node1.
Appelle PerformCleanup pour toutes les métadonnées sur Node1. Les métadonnées pour la suppression de l'étape précédente sont nettoyées.
metadataCleanup.RetentionInDays = 0; cleanupSuccessful = metadataCleanup.PerformCleanup();
metadataCleanup.RetentionInDays = 0 cleanupSuccessful = metadataCleanup.PerformCleanup()
Essaie de synchroniser Node1 et Node3, et Node2 et Node3. La synchronisation échoue, car la connaissance de synchronisation ne correspond plus à l'état du nœud. Une exception de type DbOutdatedSyncException est levée.
Il est important de ne nettoyer que les métadonnées qui ne sont plus requises par d'autres nœuds. Si le deuxième nettoyage s'était produit après réception par Node1 de la suppression de Node3, la synchronisation aurait réussi.
Important
L'exécution de l'exemple de code suivant laisse intentionnellement les exemples de bases de données dans un état incohérent. Après avoir exécuté ce code, supprimez les bases de données et recréez-les en exécutant le premier script dans Scripts d'installation pour les rubriques de procédures sur le fournisseur de bases de données.
// NOTE: Before running this application, run the database sample script that is
// available in the documentation. The script drops and re-creates the tables that
// are used in the code, and ensures that synchronization objects are dropped so that
// Sync Framework can re-create them.
using System;
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);
// Create a scope named "customer", and add the Customer and CustomerContact
// tables to the scope.
// GetDescriptionForTable gets the schema of the table, so that tracking
// tables and triggers can be created for that table.
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customer");
scopeDesc.Tables.Add(
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn));
scopeDesc.Tables.Add(
SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn));
// Create a provisioning object for "customer" and specify that
// base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1).
SqlSyncScopeProvisioning serverConfig = new SqlSyncScopeProvisioning(scopeDesc);
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip);
// Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn);
// Retrieve scope information from the server and use the schema that is retrieved
// to provision the SQL Server and SQL Server Compact client databases.
// This database already exists on the server.
DbSyncScopeDescription clientSqlDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn);
SqlSyncScopeProvisioning clientSqlConfig = new SqlSyncScopeProvisioning(clientSqlDesc);
clientSqlConfig.Apply(clientSqlConn);
// This database does not yet exist.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, true);
DbSyncScopeDescription clientSqlCeDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn);
SqlCeSyncScopeProvisioning clientSqlCeConfig = new SqlCeSyncScopeProvisioning(clientSqlCeDesc);
clientSqlCeConfig.Apply(clientSqlCe1Conn);
// Initial synchronization sessions.
SampleSyncOrchestrator syncOrchestrator;
SyncOperationStatistics syncStats;
// Data is downloaded from the server to the SQL Server client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("customer", clientSqlConn),
new SqlSyncProvider("customer", serverConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "initial");
// Data is downloaded from the SQL Server client to the
// SQL Server Compact client.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("customer", clientSqlCe1Conn),
new SqlSyncProvider("customer", clientSqlConn)
);
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");
SqlSyncStoreMetadataCleanup metadataCleanup = new SqlSyncStoreMetadataCleanup(serverConn);
bool cleanupSuccessful;
metadataCleanup.RetentionInDays = 7;
cleanupSuccessful = metadataCleanup.PerformCleanup();
if (cleanupSuccessful == true)
{
Console.WriteLine(String.Empty);
Console.WriteLine("Metadata cleanup ran in the database.");
Console.WriteLine("Metadata more than 7 days old was deleted.");
}
else
{
Console.WriteLine("Metadata cleanup failed, most likely due to concurrency issues.");
}
// Synchronize the three changes.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("customer", clientSqlConn),
new SqlSyncProvider("customer", serverConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("customer", clientSqlConn),
new SqlCeSyncProvider("customer", clientSqlCe1Conn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "CustomerContact");
metadataCleanup.RetentionInDays = 0;
cleanupSuccessful = metadataCleanup.PerformCleanup();
if (cleanupSuccessful == true)
{
Console.WriteLine(String.Empty);
Console.WriteLine("Metadata cleanup ran in the database.");
Console.WriteLine("All metadata was deleted.");
}
else
{
Console.WriteLine("Metadata cleanup failed, most likely due to concurrency issues.");
}
try
{
// Synchronize a final time.
syncOrchestrator = new SampleSyncOrchestrator(
new SqlCeSyncProvider("customer", clientSqlCe1Conn),
new SqlSyncProvider("customer", serverConn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
syncOrchestrator = new SampleSyncOrchestrator(
new SqlSyncProvider("customer", clientSqlConn),
new SqlCeSyncProvider("customer", clientSqlCe1Conn)
);
syncStats = syncOrchestrator.Synchronize();
syncOrchestrator.DisplayStats(syncStats, "subsequent");
}
catch (DbOutdatedSyncException ex)
{
Console.WriteLine(String.Empty);
Console.WriteLine("Synchronization failed due to outdated synchronization knowledge,");
Console.WriteLine("which is expected in this sample application.");
Console.WriteLine("Drop and recreate the sample databases.");
Console.WriteLine(String.Empty);
Console.WriteLine("Outdated Knowledge: " + ex.OutdatedPeerSyncKnowledge.ToString() +
" Clean up knowledge: " + ex.MissingCleanupKnowledge.ToString());
Console.WriteLine(String.Empty);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
serverConn.Close();
serverConn.Dispose();
clientSqlConn.Close();
clientSqlConn.Dispose();
clientSqlCe1Conn.Close();
clientSqlCe1Conn.Dispose();
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);
}
}
}
' NOTE: Before running this application, run the database sample script that is
' available in the documentation. The script drops and re-creates the tables that
' are used in the code, and ensures that synchronization objects are dropped so that
' Sync Framework can re-create them.
Imports System
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)
' Create a scope named "customer", and add the Customer and CustomerContact
' tables to the scope.
' GetDescriptionForTable gets the schema of the table, so that tracking
' tables and triggers can be created for that table.
Dim scopeDesc As New DbSyncScopeDescription("customer")
scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn))
scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.CustomerContact", serverConn))
' Create a provisioning object for "customer" and specify that
' base tables should not be created (They already exist in SyncSamplesDb_SqlPeer1).
Dim serverConfig As New SqlSyncScopeProvisioning(scopeDesc)
serverConfig.SetCreateTableDefault(DbSyncCreationOption.Skip)
' Configure the scope and change-tracking infrastructure.
serverConfig.Apply(serverConn)
' Retrieve scope information from the server and use the schema that is retrieved
' to provision the SQL Server and SQL Server Compact client databases.
' This database already exists on the server.
Dim clientSqlDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn)
Dim clientSqlConfig As New SqlSyncScopeProvisioning(clientSqlDesc)
clientSqlConfig.Apply(clientSqlConn)
' This database does not yet exist.
Utility.DeleteAndRecreateCompactDatabase(Utility.ConnStr_SqlCeSync1, True)
Dim clientSqlCeDesc As DbSyncScopeDescription = SqlSyncDescriptionBuilder.GetDescriptionForScope("customer", serverConn)
Dim clientSqlCeConfig As New SqlCeSyncScopeProvisioning(clientSqlCeDesc)
clientSqlCeConfig.Apply(clientSqlCe1Conn)
' Initial synchronization sessions.
Dim syncOrchestrator As SampleSyncOrchestrator
Dim syncStats As SyncOperationStatistics
' Data is downloaded from the server to the SQL Server client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("customer", clientSqlConn), _
New SqlSyncProvider("customer", serverConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "initial")
' Data is downloaded from the SQL Server client to the
' SQL Server Compact client.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("customer", clientSqlCe1Conn), _
New SqlSyncProvider("customer", clientSqlConn))
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")
Dim metadataCleanup As New SqlSyncStoreMetadataCleanup(serverConn)
Dim cleanupSuccessful As Boolean
metadataCleanup.RetentionInDays = 7
cleanupSuccessful = metadataCleanup.PerformCleanup()
If cleanupSuccessful = True Then
Console.WriteLine([String].Empty)
Console.WriteLine("Metadata cleanup ran in the database.")
Console.WriteLine("Metadata more than 7 days old was deleted.")
Else
Console.WriteLine("Metadata cleanup failed, most likely due to concurrency issues.")
End If
' Synchronize the three changes.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("customer", clientSqlConn), _
New SqlSyncProvider("customer", serverConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("customer", clientSqlConn), _
New SqlCeSyncProvider("customer", clientSqlCe1Conn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
Utility.MakeDataChangesOnNode(Utility.ConnStr_SqlSync_Server, "CustomerContact")
metadataCleanup.RetentionInDays = 0
cleanupSuccessful = metadataCleanup.PerformCleanup()
If cleanupSuccessful = True Then
Console.WriteLine([String].Empty)
Console.WriteLine("Metadata cleanup ran in the database.")
Console.WriteLine("All metadata was deleted.")
Else
Console.WriteLine("Metadata cleanup failed, most likely due to concurrency issues.")
End If
Try
' Synchronize a final time.
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlCeSyncProvider("customer", clientSqlCe1Conn), _
New SqlSyncProvider("customer", serverConn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
syncOrchestrator = New SampleSyncOrchestrator( _
New SqlSyncProvider("customer", clientSqlConn), _
New SqlCeSyncProvider("customer", clientSqlCe1Conn))
syncStats = syncOrchestrator.Synchronize()
syncOrchestrator.DisplayStats(syncStats, "subsequent")
Catch ex As DbOutdatedSyncException
Console.WriteLine([String].Empty)
Console.WriteLine("Synchronization failed due to outdated synchronization knowledge,")
Console.WriteLine("which is expected in this sample application.")
Console.WriteLine("Drop and recreate the sample databases.")
Console.WriteLine([String].Empty)
Console.WriteLine(("Outdated Knowledge: " & ex.OutdatedPeerSyncKnowledge.ToString() & " Clean up knowledge: ") + ex.MissingCleanupKnowledge.ToString())
Console.WriteLine([String].Empty)
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
serverConn.Close()
serverConn.Dispose()
clientSqlConn.Close()
clientSqlConn.Dispose()
clientSqlCe1Conn.Close()
clientSqlCe1Conn.Dispose()
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