共用方式為


HOW TO:清除共同作業同步處理的中繼資料 (SQL Server)

本主題描述如何清除使用 Sync Framework 同步處理之 SQL Server 和 SQL Server Compact 資料庫的中繼資料。本主題的程式碼著重於下列 Sync Framework 類別:

如需如何執行範例程式碼的詳細資訊,請參閱同步處理 SQL Server 和 SQL Server Compact 中的<HOW-TO 主題中的範例應用程式>。

了解中繼資料清除

清除包括刪除已自基底資料表中刪除之資料列的中繼資料。Sync Framework 會使用兩種中繼資料:

  • 追蹤每個同步處理之資料表的插入、更新和刪除項目的資料表層級中繼資料。

    基底資料表中每個資料列都有一個中繼資料列。如果資料列已自基底資料表中刪除,而且所有範圍中的所有節點都已接到,便可以安全地刪除中繼資料列。

  • 追蹤每個節點從其他節點所接到之變更的資料庫層級中繼資料。

    這項中繼資料一般會儲存在每個節點資料庫的一個範圍資料表中。範圍資料表中的資料列絕對不可刪除,除非卸除此範圍。

清除是以保留為基礎,這表示系統會刪除比指定之天數還要舊的中繼資料。若為 SQL Server 資料庫,請使用 SqlSyncStoreMetadataCleanup 物件。若為 SQL Server Compact 資料庫,請使用 SqlCeSyncStoreMetadataCleanup 物件。這兩個物件具有相同的屬性和方法。

SQL Server SQL Server Compact 描述

PerformCleanup

PerformCleanup

您從應用程式呼叫來清除中繼資料的方法。

RetentionInDays

RetentionInDays

指定當呼叫清除方法時要刪除多久之前之變更追蹤中繼資料 (以天為單位) 的屬性。

如果某個節點嘗試同步處理中繼資料已經清除的變更,便會擲回 DbOutdatedSyncException 類型的例外狀況。隨即引發 SyncPeerOutdated 事件,此事件會提供 DbOutdatedEventArgs 物件的存取權。有兩個選項可處理這個事件:

  • Action 屬性設定為 PartialSync。這樣會針對有中繼資料存在的資料進行同步處理,但會遺漏某些刪除。

  • 請將 Action 屬性設定為 AbortSync (預設值)。這樣會結束同步處理工作階段。用戶端應該在下一個同步處理工作階段重新初始化,以便讓它擁有正確的資料。

完整的程式碼範例

完整的程式碼範例會執行下列步驟:

  1. 同步處理 SyncSamplesDb_SqlPeer1 (Node1) 和 SyncSamplesDb_SqlPeer1 (Node2)。將九個資料列上傳到 Node2

  2. 同步處理 Node2SyncSampleClient1.sdf (Node3)。

  3. Node1 上執行插入、更新和刪除作業。

  4. 針對 Node1 上超過 7 天以上的中繼資料,呼叫 PerformCleanupPerformCleanup 方法順利傳回,但是沒有清除任何中繼資料,因為沒有在 Node1 上進行任何超過 7 天的刪除作業。

    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. 同步處理 Node1Node3,以及 Node2Node3。同步處理成功,因為所有相關中繼資料仍然可在兩個節點上使用。

  6. Node1 中刪除資料列。

  7. 針對 Node1 上的所有中繼資料,呼叫 PerformCleanup。上一個步驟中刪除的中繼資料已清除。

    metadataCleanup.RetentionInDays = 0;
    cleanupSuccessful = metadataCleanup.PerformCleanup();
    
    metadataCleanup.RetentionInDays = 0
    cleanupSuccessful = metadataCleanup.PerformCleanup()
    
  8. 嘗試同步處理 Node1Node3,以及 Node2Node3。同步處理失敗,因為同步處理知識不再符合節點的狀態。擲回 DbOutdatedSyncException 型別的例外狀況。

只能清除其他節點不再需要的中繼資料,這一點很重要。如果第二個清除發生在 Node1 已從 Node3 接到刪除之後,同步處理就會成功。

注意

執行下列範例程式碼是有意讓範例資料庫處於不一致的狀態。在您執行此程式碼之後,請卸除資料庫,然後執行資料庫提供者的安裝指令碼 HOW-TO 主題中的第一個指令碼,重新建立資料庫。

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

請參閱

概念

同步處理 SQL Server 和 SQL Server Compact