Compartilhar via


Rastreando o processo de sincronização

Este tópico mostra como usar a infraestrutura de rastreamento e a classe de rastreamento do Sync Framework. Os exemplos neste tópico se concentram nos seguintes eventos e tipos do Sync Framework:

Para obter informações sobre como executar o código de exemplo, consulte "Exemplo de aplicativos nos tópicos de instruções" em Sincronizando o SQL Server e o SQL Server Compact.

Noções básicas sobre o rastreamento nos serviços de sincronização

O rastreamento envolve registrar operações, dados e metadados de aplicativo e apresentar essas informações a um ouvinte. Um ouvinte costuma gravar informações de rastreamento em um arquivo, mas também pode trabalhar com elas de outras maneiras. O Sync Framework inclui rastreamento para os provedores de sincronização do banco de dados. Em aplicativos distribuídos, o rastreamento pode ser muito importante porque permite solucionar problemas que poderiam ser difíceis de descobrir.

No Sync Framework o rastreamento é formado pelos seguintes componentes:

  • Uma infraestrutura de rastreamento baseada na implementação de rastreamento .NET Framework, especificamente a classe TraceListener. As operações mais importantes dos provedores do banco de dados são rastreadas, e os principais metadados são fornecidos para um ou mais ouvintes.

  • O objeto SyncTracer. Ele permite determinar qual nível de rastreamento está habilitado e gravar mensagens na saída de rastreamento com base em eventos de aplicativo.

Além dos componentes de rastreamento oferecidos pelo Sync Framework, a solução de problemas geralmente envolve outras ferramentas, como um depurador ou o SQL Server Profiler. Por exemplo, a saída de rastreamento poderia incluir informações sobre um banco de dados do SQL Server. Nesse caso, você usaria o SQL Server Profiler para obter mais detalhes sobre a atividade de banco de dados gerada pelo provedor de sincronização do servidor.

Usando a infraestrutura de rastreamento

Por padrão, o rastreamento não está habilitado para aplicativos do Sync Framework. Para configurar o ouvinte de rastreamento, inclua um arquivo de configuração de aplicativo (AppName.exe.config) na mesma pasta do seu executável do aplicativo gerenciado. Para obter mais informações sobre esse arquivo, consulte a documentação do Visual Studio. Nesse arquivo, é possível adicionar um ouvinte, definir seu tipo e os parâmetros relacionados, remover um ouvinte ou limpar todos os ouvintes que anteriormente foram definidos pelo aplicativo. Para obter mais informações, consulte a documentação do .NET Framework sobre rastreamento. O arquivo de configuração deve ser parecido com o exemplo a seguir.

<configuration>
  <system.diagnostics>
    <switches>
      <!--  0-off, 1-error, 2-warn, 3-info, 4-verbose. -->
      <add name="SyncTracer" value="3" />
    </switches>

    <trace autoflush="true">
      <listeners>
        <add name="TestListener" type="System.Diagnostics.TextWriterTraceListener" initializeData="TraceSample.log"/>
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

Os seguintes nós XML são incluídos no código de exemplo:

  • Um ouvinte do tipo System.Diagnostics.TextWriterTraceListener (TextWriterTraceListener), com o nome do arquivo de saída: TraceSample.log.

  • Uma opção chamada SyncTracer. Ela tem um valor igual a 3. A tabela a seguir mostra todos os valores e como eles estão relacionados com a saída de rastreamento.

    Valor da opção

    Nível de rastreamento

    Saída

    0

    off

    Nenhuma mensagem para os ouvintes de rastreamento.

    1

    error

    Apenas mensagens de erro para os ouvintes de rastreamento.

    2

    warning

    Mensagens de erro e de aviso para os ouvintes de rastreamento.

    3

    info

    Mensagens informativas, de aviso e de erro para os ouvintes de rastreamento.

    4

    verbose

    Todas as mensagens para os ouvintes de rastreamento.

    O rastreamento está com um pouco de sobrecarga. Por isso, você deve equilibrar o rastreamento em relação aos requisitos de desempenho do aplicativo. Na maioria dos casos, as configurações info e verbose só são apropriadas durante o desenvolvimento e a solução de problemas do aplicativo.

O arquivo de configuração a seguir mostra um exemplo para dispositivos.

<configuration>

   <traceSettings>

     <add key ="FileLocation" value="TraceSample.log"/>

     <add key ="LogLevel" value="4"/>

   </traceSettings>

</configuration>

O arquivo deve ser denominado trace.config.txt e deve ser colocado no diretório do aplicativo no dispositivo. Se você incluir o arquivo em uma solução do Visual Studio, ele pode ser implantado com o aplicativo.

O segmento de arquivo abaixo é de um rastreamento que foi configurado usando um valor SyncTracer igual a 3. Cada linha começa com o tipo de saída. Nesse caso, toda a saída é INFO. Se ocorresse um erro, as linhas relevantes começariam com ERROR.

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:027, Connecting to server using string: Data Source=localhost;Initial Catalog=SyncSamplesDb;Integrated Security=True

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:027, ----- Server Enumerating Changes to Client for Group "Customer" -----

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:027, Client Id: bc5610f6-bf9c-4ccd-8599-839e54e953e2

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:027, Mapped Originator Id: 0

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:042,

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:042, ----- Enumerating Inserts for Table Customer -----

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:058, Changes Enumerated: 5

INFO, MyApp.vshost, 10, 07/01/2010 17:43:03:058, --- End Enumerating Inserts for Table Customer ---

Usando o objeto SyncTracer

O objeto SyncTracer permite gravar dados de rastreamento específicos do aplicativo no arquivo de rastreamento. Isso pode fornecer informações contextuais sobre a atividade de um aplicativo em um determinado horário. Este objeto permite executar as seguintes tarefas:

  • Determinar que nível de rastreamento está habilitado, usando estes métodos:

  • Gravar mensagens na saída de rastreamento com base em eventos de aplicativo, usando os métodos abaixo, e outras sobrecargas relacionadas a cada método:

    Por exemplo, para gerar uma mensagem informativa, você pode usar o seguinte código:

    SyncTracer.Info("Informational message")

    Se o nível Informações estiver habilitado, a mensagem será gravada na saída; do contrário, será ignorada.

    Mensagens mais complexas também podem ser formadas, como as mostradas a seguir. Os números especificados definem o nível de recuo do arquivo de saída.

    SyncTracer.Verbose("Processing table t1")

    SyncTracer.Verbose(1, "Applying Deletes")

    SyncTracer.Verbose(2, "{0} rows deleted", numDeleted)

    SyncTracer.Verbose(1, "Applying Inserts")

    SyncTracer.Verbose(2, "{0} rows inserted", numInserted)

    A saída é a seguinte:

    Processing table t1

    Applying Deletes

    7 Rows Deleted

    Applying Inserts

    9 Rows inserted

O exemplo de código a seguir grava informações no console sobre quais níveis de rastreamento estão habilitados. O arquivo de configuração especifica um valor igual a 3 para a opção SyncTracer. Isso corresponde a Info. Portanto, Error, Warning e Info retornam True e Verbose retorna False.

Console.WriteLine("** Tracing Levels Enabled for this Application **");
Console.WriteLine("Error: " + SyncTracer.IsErrorEnabled().ToString());
Console.WriteLine("Warning: " + SyncTracer.IsWarningEnabled().ToString());
Console.WriteLine("Info: " + SyncTracer.IsInfoEnabled().ToString());
Console.WriteLine("Verbose: " + SyncTracer.IsVerboseEnabled().ToString());
Console.WriteLine("** Tracing Levels Enabled for this Application **")
Console.WriteLine("Error: " + SyncTracer.IsErrorEnabled().ToString())
Console.WriteLine("Warning: " + SyncTracer.IsWarningEnabled().ToString())
Console.WriteLine("Info: " + SyncTracer.IsInfoEnabled().ToString())
Console.WriteLine("Verbose: " + SyncTracer.IsVerboseEnabled().ToString())

O exemplo de código a seguir mostra como gravar mensagens de aviso formatadas sobre conflitos de dados na saída de rastreamento. Para obter mais informações sobre conflitos, consulte Como manipular conflitos de dados e erros de sincronização de bancos de dados (SQL Server). O rastreamento detalhado inclui informações sobre conflitos. Neste aplicativo, o rastreamento detalhado está desabilitado, e o aplicativo marca os conflitos com um aviso.

if (SyncTracer.IsVerboseEnabled() == false && e.Conflict.Type != DbConflictType.ErrorsOccurred)
{
    DataTable conflictingClientChange = e.Conflict.LocalChange;
    DataTable conflictingServerChange = e.Conflict.RemoteChange;
    int serverColumnCount = conflictingServerChange.Columns.Count;
    int clientColumnCount = conflictingClientChange.Columns.Count;
    StringBuilder clientRowAsString = new StringBuilder();
    StringBuilder serverRowAsString = new StringBuilder();

    for (int i = 0; i < clientColumnCount; i++)
    {
        clientRowAsString.Append(conflictingClientChange.Rows[0][i] + " | ");
    }

    for (int i = 0; i < serverColumnCount; i++)
    {
        serverRowAsString.Append(conflictingServerChange.Rows[0][i] + " | ");
    }

    SyncTracer.Warning(1, "CONFLICT DETECTED FOR SESSION {0}", e.Session.SessionId);
    SyncTracer.Warning(2, "** Client change **");
    SyncTracer.Warning(2, clientRowAsString.ToString());
    SyncTracer.Warning(2, "** Server change **");
    SyncTracer.Warning(2, serverRowAsString.ToString());
}
If SyncTracer.IsVerboseEnabled() = False AndAlso e.Conflict.Type <> DbConflictType.ErrorsOccurred Then
    Dim conflictingClientChange As DataTable = e.Conflict.LocalChange
    Dim conflictingServerChange As DataTable = e.Conflict.RemoteChange
    Dim serverColumnCount As Integer = conflictingServerChange.Columns.Count
    Dim clientColumnCount As Integer = conflictingClientChange.Columns.Count
    Dim clientRowAsString As New StringBuilder()
    Dim serverRowAsString As New StringBuilder()

    For i As Integer = 0 To clientColumnCount - 1
        clientRowAsString.Append(Convert.ToString(conflictingClientChange.Rows(0)(i)) & " | ")
    Next

    For i As Integer = 0 To serverColumnCount - 1
        serverRowAsString.Append(Convert.ToString(conflictingServerChange.Rows(0)(i)) & " | ")
    Next

    SyncTracer.Warning(1, "CONFLICT DETECTED FOR SESSION {0}", e.Session.SessionId)
    SyncTracer.Warning(2, "** Client change **")
    SyncTracer.Warning(2, clientRowAsString.ToString())
    SyncTracer.Warning(2, "** Server change **")
    SyncTracer.Warning(2, serverRowAsString.ToString())
End If

Verificar quais níveis de rastreamento estão habilitados pode ajudar você a evitar um processamento possivelmente dispendioso. No código do exemplo, o aplicativo evita processamento adicional se o rastreamento detalhado já está habilitado. De modo contrário, o aplicativo só poderá gerar saída quando um certo nível de rastreamento estiver habilitado.

Considerações de segurança do rastreamento

Os arquivos de rastreamento podem incluir informações sobre computadores cliente e servidor, dados de aplicativo e logons. (As senhas não são gravadas no arquivo de rastreamento.) Se o rastreamento detalhado estiver habilitado, cada linha alterada do banco de dados será gravada no arquivo de rastreamento. Para ajudar a proteger o arquivo de rastreamento, use as listas de controle de acesso apropriadas.

Exemplo de código completo

O exemplo de código completo a seguir inclui os exemplos de código descritos anteriormente neste tópico e código adicional para executar a sincronização. Antes de executar o aplicativo, execute estas etapas:

  1. Crie um projeto no Visual Studio.

  2. Adicione referências às DLLs do Sync Framework e à classe Utility que está disponível em Classe de utilitário para tópicos de instruções do provedor de banco de dados.

  3. Crie um arquivo de configuração e copie o código XML do exemplo mostrado anteriormente neste tópico.

Conheça as chamadas para métodos na classe Utility:

  • Utility.MakeFailingChangeOnNode(Utility.ConnStr_SqlSync_Client) Isso faz uma alteração no cliente que falha quando aplicada ao servidor. A violação de restrição e a exceção de aplicativo relacionada são gravadas automaticamente no arquivo de rastreamento como avisos.

  • Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Client, "Customer") e Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Server, "Customer") Eles fazem alterações no cliente e no servidor que entram em conflito quando são sincronizadas. Os conflitos são gravados no arquivo de rastreamento, no manipulador de eventos SampleSyncProvider_ApplyChangeFailed.

Depois de executar o aplicativo, abra o arquivo de rastreamento de saída para ver as mensagens gravadas automaticamente e os avisos de conflito, que são o resultado do código de aplicativo.

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)
        {
            // Specify the connections to the SQL Server and SQL Server Compact databases.
            SqlConnection serverConn = new SqlConnection(Utility.ConnStr_SqlSync_Server);
            SqlConnection clientConn = new SqlConnection(Utility.ConnStr_SqlSync_Client);

            //Write to the console which tracing levels are enabled. The app.config
            //file specifies a value of 3 for the SyncTracer switch, which corresponds
            //to Info. Therefore, Error, Warning, and Info return True, and Verbose
            //returns False.
            Console.WriteLine("");
            Console.WriteLine("** Tracing Levels Enabled for this Application **");
            Console.WriteLine("Error: " + SyncTracer.IsErrorEnabled().ToString());
            Console.WriteLine("Warning: " + SyncTracer.IsWarningEnabled().ToString());
            Console.WriteLine("Info: " + SyncTracer.IsInfoEnabled().ToString());
            Console.WriteLine("Verbose: " + SyncTracer.IsVerboseEnabled().ToString());

            // Create a scope named "customers" and add a table to it.
            DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription("customers");

            DbSyncTableDescription customerDescription =
                SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn);

            scopeDesc.Tables.Add(customerDescription);

            // Create a provisioning object for "customers". We specify 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(serverConn, scopeDesc);
            serverConfig.ObjectSchema = "Sync";

            // Configure the scope and change-tracking infrastructure.
            serverConfig.Apply();

            // Provision the client database.
            SqlSyncScopeProvisioning clientConfig = new SqlSyncScopeProvisioning(clientConn, scopeDesc);
            clientConfig.ObjectSchema = "Sync";
            clientConfig.Apply();

            // Initial synchronization session.
            SampleSyncOrchestrator syncOrchestrator;
            SyncOperationStatistics syncStats;

            // Data is downloaded from the server to the client.
            syncOrchestrator = new SampleSyncOrchestrator(
                new SqlSyncProvider("customers", clientConn, null, "Sync"),
                new SqlSyncProvider("customers", serverConn, null, "Sync")
                );
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "initial");


            //Make a change at the client that fails when it is
            //applied at the servr. The constraint violation
            //is automatically written to the trace file as a warning.
            Utility.MakeFailingChangeOnNode(Utility.ConnStr_SqlSync_Client);

            //Make changes at the client and server that conflict
            //when they are synchronized. The conflicts are written
            //to the trace file in the SampleSyncProvider_ApplyChangeFailed
            //event handler.
            Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Client, "Customer");
            Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Server, "Customer");

            SqlSyncProvider clientProv = new SqlSyncProvider("customers", clientConn, null, "Sync");
            clientProv.ApplyChangeFailed += new EventHandler<DbApplyChangeFailedEventArgs>(SampleSyncProvider_ApplyChangeFailed);

            SqlSyncProvider serverProv = new SqlSyncProvider("customers", serverConn, null, "Sync");
            serverProv.ApplyChangeFailed += new EventHandler<DbApplyChangeFailedEventArgs>(SampleSyncProvider_ApplyChangeFailed);

            // Synchronize the two databases.
            syncOrchestrator = new SampleSyncOrchestrator(clientProv, serverProv);
            syncStats = syncOrchestrator.Synchronize();
            syncOrchestrator.DisplayStats(syncStats, "subsequent");

            serverConn.Close();
            serverConn.Dispose();
            clientConn.Close();
            clientConn.Dispose();

            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Server);
            Utility.CleanUpSqlNode(Utility.ConnStr_SqlSync_Client);

            Console.Write("\nPress any key to exit.");
            Console.Read();
        }

        static void SampleSyncProvider_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e)
        {
            //Verbose tracing includes information about conflicts. In this application,
            //Verbose tracing is disabled, and we have decided to flag conflicts with a 
            //warning.
            //Check if Verbose tracing is enabled and if the conflict is an error.
            //If the conflict is not an error, we write a warning to the trace file
            //with information about the conflict.
            if (SyncTracer.IsVerboseEnabled() == false && e.Conflict.Type != DbConflictType.ErrorsOccurred)
            {
                DataTable conflictingClientChange = e.Conflict.LocalChange;
                DataTable conflictingServerChange = e.Conflict.RemoteChange;
                int serverColumnCount = conflictingServerChange.Columns.Count;
                int clientColumnCount = conflictingClientChange.Columns.Count;
                StringBuilder clientRowAsString = new StringBuilder();
                StringBuilder serverRowAsString = new StringBuilder();

                for (int i = 0; i < clientColumnCount; i++)
                {
                    clientRowAsString.Append(conflictingClientChange.Rows[0][i] + " | ");
                }

                for (int i = 0; i < serverColumnCount; i++)
                {
                    serverRowAsString.Append(conflictingServerChange.Rows[0][i] + " | ");
                }

                SyncTracer.Warning(1, "CONFLICT DETECTED FOR SESSION {0}", e.Session.SessionId);
                SyncTracer.Warning(2, "** Client change **");
                SyncTracer.Warning(2, clientRowAsString.ToString());
                SyncTracer.Warning(2, "** Server change **");
                SyncTracer.Warning(2, serverRowAsString.ToString());
            }
        }
    }

    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.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())
            ' Specify the connections to the SQL Server and SQL Server Compact databases.
            Dim serverConn As New SqlConnection(Utility.ConnStr_SqlSync_Server)
            Dim clientConn As New SqlConnection(Utility.ConnStr_SqlSync_Client)

            'Write to the console which tracing levels are enabled. The app.config
            'file specifies a value of 3 for the SyncTracer switch, which corresponds
            'to Info. Therefore, Error, Warning, and Info return True, and Verbose
            'returns False.
            Console.WriteLine("")
            Console.WriteLine("** Tracing Levels Enabled for this Application **")
            Console.WriteLine("Error: " + SyncTracer.IsErrorEnabled().ToString())
            Console.WriteLine("Warning: " + SyncTracer.IsWarningEnabled().ToString())
            Console.WriteLine("Info: " + SyncTracer.IsInfoEnabled().ToString())
            Console.WriteLine("Verbose: " + SyncTracer.IsVerboseEnabled().ToString())

            ' Create a scope named "customers" and add a table to it.
            Dim scopeDesc As New DbSyncScopeDescription("customers")

            Dim customerDescription As DbSyncTableDescription = SqlSyncDescriptionBuilder.GetDescriptionForTable("Sales.Customer", serverConn)

            scopeDesc.Tables.Add(customerDescription)

            ' Create a provisioning object for "customers". We specify 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(serverConn, scopeDesc)
            serverConfig.ObjectSchema = "Sync"

            ' Configure the scope and change-tracking infrastructure.
            serverConfig.Apply()

            ' Provision the client database.
            Dim clientConfig As New SqlSyncScopeProvisioning(clientConn, scopeDesc)
            clientConfig.ObjectSchema = "Sync"
            clientConfig.Apply()

            ' Initial synchronization session.
            Dim syncOrchestrator As SampleSyncOrchestrator
            Dim syncStats As SyncOperationStatistics

            ' Data is downloaded from the server to the client.
            syncOrchestrator = New SampleSyncOrchestrator(New SqlSyncProvider("customers", clientConn, Nothing, "Sync"), New SqlSyncProvider("customers", serverConn, Nothing, "Sync"))
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "initial")


            'Make a change at the client that fails when it is
            'applied at the servr. The constraint violation
            'is automatically written to the trace file as a warning.
            Utility.MakeFailingChangeOnNode(Utility.ConnStr_SqlSync_Client)

            'Make changes at the client and server that conflict
            'when they are synchronized. The conflicts are written
            'to the trace file in the SampleSyncProvider_ApplyChangeFailed
            'event handler.
            Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Client, "Customer")
            Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Server, "Customer")

            Dim clientProv As New SqlSyncProvider("customers", clientConn, Nothing, "Sync")
            AddHandler clientProv.ApplyChangeFailed, AddressOf SampleSyncProvider_ApplyChangeFailed

            Dim serverProv As New SqlSyncProvider("customers", serverConn, Nothing, "Sync")
            AddHandler serverProv.ApplyChangeFailed, AddressOf SampleSyncProvider_ApplyChangeFailed

            ' Synchronize the two databases.
            syncOrchestrator = New SampleSyncOrchestrator(clientProv, serverProv)
            syncStats = syncOrchestrator.Synchronize()
            syncOrchestrator.DisplayStats(syncStats, "subsequent")

            serverConn.Close()
            serverConn.Dispose()
            clientConn.Close()
            clientConn.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

        Private Shared Sub SampleSyncProvider_ApplyChangeFailed(ByVal sender As Object, ByVal e As DbApplyChangeFailedEventArgs)
            'Verbose tracing includes information about conflicts. In this application,
            'Verbose tracing is disabled, and we have decided to flag conflicts with a 
            'warning.
            'Check if Verbose tracing is enabled and if the conflict is an error.
            'If the conflict is not an error, we write a warning to the trace file
            'with information about the conflict.
            If SyncTracer.IsVerboseEnabled() = False AndAlso e.Conflict.Type <> DbConflictType.ErrorsOccurred Then
                Dim conflictingClientChange As DataTable = e.Conflict.LocalChange
                Dim conflictingServerChange As DataTable = e.Conflict.RemoteChange
                Dim serverColumnCount As Integer = conflictingServerChange.Columns.Count
                Dim clientColumnCount As Integer = conflictingClientChange.Columns.Count
                Dim clientRowAsString As New StringBuilder()
                Dim serverRowAsString As New StringBuilder()

                For i As Integer = 0 To clientColumnCount - 1
                    clientRowAsString.Append(Convert.ToString(conflictingClientChange.Rows(0)(i)) & " | ")
                Next

                For i As Integer = 0 To serverColumnCount - 1
                    serverRowAsString.Append(Convert.ToString(conflictingServerChange.Rows(0)(i)) & " | ")
                Next

                SyncTracer.Warning(1, "CONFLICT DETECTED FOR SESSION {0}", e.Session.SessionId)
                SyncTracer.Warning(2, "** Client change **")
                SyncTracer.Warning(2, clientRowAsString.ToString())
                SyncTracer.Warning(2, "** Server change **")
                SyncTracer.Warning(2, serverRowAsString.ToString())
            End If
        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: " & Convert.ToString(syncStatistics.SyncStartTime))
            Console.WriteLine("Total Changes Uploaded: " & Convert.ToString(syncStatistics.UploadChangesTotal))
            Console.WriteLine("Total Changes Downloaded: " & Convert.ToString(syncStatistics.DownloadChangesTotal))
            Console.WriteLine("Complete Time: " & Convert.ToString(syncStatistics.SyncEndTime))
            Console.WriteLine([String].Empty)
        End Sub
    End Class
End Namespace

Consulte também

Outros recursos

Considerações sobre implantação e design de aplicativos