Freigeben über


Vorgehensweise: Bereinigen von Metadaten zur Synchronisierung für die Zusammenarbeit (SQL Server)

In diesem Thema wird das Bereinigen von Metadaten für SQL Server und SQL Server Compact-Datenbanken beschrieben, die mithilfe von Sync Framework synchronisiert werden. Der Code für dieses Thema ist auf die folgenden Sync Framework-Klassen ausgerichtet:

Weitere Informationen dazu, wie Sie Beispielcode ausführen können, finden Sie unter „Beispielanwendungen in den Themen zur Vorgehensweise“ in Synchronisieren von SQL Server und SQL Server Compact.

Grundlegendes zur Metadatenbereinigung

Bei der Bereinigung werden Metadaten für Zeilen gelöscht, die aus einer Basistabelle gelöscht wurden. Sync Framework verwendet zwei Arten von Metadaten:

  • Metadaten auf Tabellenebene, mit denen Einfügungen, Aktualisierungen und Löschvorgänge für jede Tabelle, die synchronisiert wird, nachverfolgt werden.

    Es gibt eine Zeile mit Metadaten für jede Zeile in der Basistabelle. Wenn eine Zeile aus der Basistabelle gelöscht wurde und alle Knoten in allen Bereichen diese erhalten haben, kann die Metadatenzeile problemlos gelöscht werden.

  • Metadaten auf Datenbankebene, die nachverfolgen, welche Änderungen jeder Knoten von anderen Knoten empfangen hat.

    Diese Metadaten werden in der Regel in einer Bereichstabelle für jede Knotendatenbank gespeichert. Zeilen in der Bereichstabelle sollten nur gelöscht werden, wenn der Bereich gelöscht wird.

Ein Cleanup ist beibehaltungsbasiert. Das bedeutet, dass Metadaten, die älter als die angegebene Anzahl von Tagen sind, gelöscht werden. Verwenden Sie für SQL Server-Datenbanken das SqlSyncStoreMetadataCleanup-Objekt und für SQL Server Compact-Datenbanken das SqlCeSyncStoreMetadataCleanup-Objekt. Beide Objekte verfügen über die gleichen Eigenschaften und Methoden.

SQL Server SQL Server Compact Beschreibung

PerformCleanup

PerformCleanup

Die Methode, die Sie in Ihrer Anwendung zur Bereinigung von Metadaten aufrufen.

RetentionInDays

RetentionInDays

Die Eigenschaft, die angibt, wie alt (in Tagen) Änderungsnachverfolgungs-Metadaten sein müssen, damit diese Metadaten beim Aufrufen der Bereinigungsmethode gelöscht werden.

Wenn ein Knoten versucht, Änderungen zu synchronisieren, deren Metadaten bereits bereinigt wurden, wird eine Ausnahme vom Typ DbOutdatedSyncException ausgelöst. Das SyncPeerOutdated-Ereignis wird ausgelöst, wodurch der Zugriff auf ein DbOutdatedEventArgs-Objekt ermöglicht wird. Zum Behandeln dieses Ereignisses bestehen zwei Möglichkeiten:

  • Legen Sie die Action-Eigenschaft auf PartialSync fest. Dadurch werden die Daten synchronisiert, für die Metadaten vorhanden sind. Einige Löschungen werden jedoch ausgelassen.

  • Legen Sie die Action-Eigenschaft auf AbortSync fest (Standardeinstellung). Dadurch wird die Synchronisierungssitzung beendet. Der Client sollte in der nächsten Synchronisierungssitzung erneut initialisiert werden, damit er über die richtigen Daten verfügt.

Vollständiges Codebeispiel

Im vollständigen Codebeispiel werden folgende Schritte ausgeführt:

  1. Synchronisieren von SyncSamplesDb_SqlPeer1 (Node1) und SyncSamplesDb_SqlPeer1 (Node2). Neun Zeilen werden auf Node2 hochgeladen.

  2. Synchronisieren von Node2 und SyncSampleClient1.sdf (Node3).

  3. Führt einen Einfüge-, Aktualisierungs- oder Löschvorgang auf Node1 aus.

  4. Aufrufen von PerformCleanup für Metadaten auf Node1, die älter als 7 Tage sind. Die PerformCleanup-Methode wird erfolgreich beendet. Es werden jedoch keine Metadaten bereinigt, da auf Node1 keine Löschungen vorgenommen wurden, die älter als 7 Tage sind.

    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()
    
  5. Synchronisieren von Node1 und Node3 sowie Node2 und Node3. Die Synchronisierung ist erfolgreich, da alle relevanten Metadaten immer noch auf beiden Knoten verfügbar sind.

  6. Löschen einer Zeile auf Node1.

  7. Aufrufen von PerformCleanup für alle Metadaten auf Node1. Die Metadaten für den Löschvorgang aus dem vorherigen Schritt werden bereinigt.

    metadataCleanup.RetentionInDays = 0;
    cleanupSuccessful = metadataCleanup.PerformCleanup();
    
    metadataCleanup.RetentionInDays = 0
    cleanupSuccessful = metadataCleanup.PerformCleanup()
    
  8. Versuch, Node1 und Node3 sowie Node2 und Node3 zu synchronisieren. Die Synchronisierung schlägt fehl, da das Synchronisierungswissen nicht mehr zum Status des Knotens passt. Eine Ausnahme des Typs DbOutdatedSyncException wird ausgelöst.

Es ist wichtig, dass nur die Metadaten bereinigt werden, die nicht mehr für andere Knoten benötigt werden. Wenn die zweite Bereinigung erfolgt wäre, nachdem der Löschvorgang von Node3 an Node1 weitergegeben wurde, wäre die Synchronisierung erfolgreich gewesen.

Wichtig

Wenn Sie den folgenden Beispielcode ausführen, sind die Beispieldatenbanken danach absichtlich inkonsistent. Löschen Sie die Datenbanken, nachdem Sie diesen Code ausgeführt haben, und erstellen Sie sie neu, indem Sie das erste Skript in Setupskripts für Datenbankanbieter - Themen zur Vorgehensweise ausführen.

// 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

Siehe auch

Konzepte

Synchronisieren von SQL Server und SQL Server Compact