이 항목에서는 Sync Framework의 추적 인프라와 추적 클래스를 사용하는 방법을 보여 줍니다. 이 항목의 예제에서는 다음과 같은 Sync Framework 형식 및 이벤트를 중점적으로 설명합니다.
샘플 코드를 실행하는 방법에 대한 자세한 내용은 SQL Server와 SQL Server Compact 동기화의 "방법 항목의 예제 응용 프로그램"을 참조하십시오.
Synchronization Services의 추적 이해
추적에는 응용 프로그램 작업, 데이터 및 메타데이터를 기록하고 이 정보를 수신기에 제공하는 작업이 포함됩니다. 리스너는 파일에 추적 정보를 기록하는 경우가 많지만 다른 방식으로 정보를 처리할 수도 있습니다. Sync Framework에는 데이터베이스 동기화 공급자용 추적 기능이 포함되어 있습니다. 분산 응용 프로그램에서 추적은 다른 방법으로는 발견하기 어려운 문제를 해결할 수 있는 매우 중요한 기능입니다.
Sync Framework의 추적은 다음과 같은 요소로 구성되어 있습니다.
추적의 .NET Framework 구현을 기반으로 하는 추적 인프라(특히 TraceListener 클래스). 데이터베이스 공급자의 가장 중요한 작업이 추적되고 주요 메타데이터가 하나 이상의 수신기에 제공됩니다.
SyncTracer 개체. 이 개체를 사용하면 활성화된 추적 수준을 확인하고 응용 프로그램 이벤트에 따라 추적 출력에 메시지를 기록할 수 있습니다.
일반적으로 문제 해결에는 Sync Framework에서 제공하는 추적 구성 요소 외에도 디버거나 SQL Server Profiler와 같은 다른 도구가 관련됩니다. 예를 들어, 추적 출력에는 SQL Server 데이터베이스에 대한 정보가 포함될 수 있으며, 이 경우 SQL Server Profiler를 사용하여 서버 동기화 공급자가 생성한 데이터베이스 작업에 대한 정보를 얻을 수 있습니다.
추적 인프라 사용
Sync Framework 응용 프로그램의 경우 기본적으로 추적이 활성화되어 있지 않습니다. 추적 수신기를 구성하려면 응용 프로그램 구성 파일(AppName.exe.config)을 관리되는 응용 프로그램 실행 파일이 있는 폴더와 동일한 폴더에 포함합니다. 이 파일에 대한 자세한 내용은 Visual Studio 설명서를 참조하십시오. 이 파일에서 수신기를 추가하거나, 수신기 유형 및 관련 매개 변수를 설정하거나, 수신기를 제거하거나, 응용 프로그램에서 이전에 설정된 수신기를 모두 지울 수 있습니다. 자세한 내용은 추적에 대한 .NET Framework 설명서를 참조하십시오. 구성 파일은 다음 예제와 유사합니다.
<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>
다음과 같은 XML 노드가 예제 코드에 포함되어 있습니다.
System.Diagnostics.TextWriterTraceListener(TextWriterTraceListener) 형식의 수신기(출력 파일 이름: TraceSample.log)
SyncTracer라는 스위치. 이 스위치의 값은 3입니다. 다음 표에서는 모든 값을 제공하고 각 값과 추적 출력의 관계를 보여 줍니다.
스위치 값
추적 수준
출력
0
off
추적 수신기에 메시지가 제공되지 않습니다.
1
error
추적 수신기에 오류 메시지만 제공됩니다.
2
warning
추적 수신기에 오류 및 경고 메시지가 제공됩니다.
3
info
추적 수신기에 알림, 경고 및 오류 메시지가 제공됩니다.
4
verbose
추적 수신기에 모든 메시지가 제공됩니다.
추적에는 오버헤드가 있으므로 응용 프로그램의 성능 요구 사항에 맞게 추적 수준을 조정해야 합니다. 대부분의 경우 info 및 verbose 설정은 응용 프로그램 개발과 문제 해결 중에만 적합합니다.
다음 구성 파일은 장치에 대한 예제를 보여줍니다.
<configuration>
<traceSettings>
<add key ="FileLocation" value="TraceSample.log"/>
<add key ="LogLevel" value="4"/>
</traceSettings>
</configuration>
파일의 이름을 trace.config.txt로 지정하여 장치의 응용 프로그램 디렉터리에 두어야 합니다. Visual Studio 솔루션에 파일을 포함하면 응용 프로그램을 사용하여 해당 파일을 배포할 수 있습니다.
다음 파일 세그먼트는 값이 3인 SyncTracer를 사용하여 구성된 추적의 일부입니다. 각 줄은 출력 유형으로 시작됩니다. 이 경우에는 모든 출력이 INFO입니다. 오류가 발생한 경우 관련 줄은 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 ---
SyncTracer 개체 사용
SyncTracer 개체를 사용하면 추적 파일에 응용 프로그램 관련 추적 데이터를 기록할 수 있습니다. 이에 따라 응용 프로그램에서 특정 시간에 수행하는 작업에 대한 컨텍스트 정보를 제공할 수 있습니다. 이 개체를 사용하여 다음 태스크를 수행할 수 있습니다.
다음 메서드를 사용하여 활성화된 추적 수준을 확인합니다.
다음 메서드와 각 메서드의 다른 오버로드를 사용하여 응용 프로그램 이벤트에 따라 추적 출력에 메시지를 기록합니다.
예를 들어, 알림 메시지를 출력하려면 다음과 같은 코드를 사용할 수 있습니다.
SyncTracer.Info("Informational message")
Info 수준이 활성화되면 메시지가 출력에 기록되고, 그렇지 않으면 메시지가 무시됩니다.
더 복잡한 메시지도 다음과 같이 구성할 수 있습니다. 지정된 숫자는 출력 파일에서 들여쓰기 수준을 설정합니다.
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)
출력은 다음과 같습니다.
Processing table t1
Applying Deletes
7 Rows Deleted
Applying Inserts
9 Rows inserted
다음 코드 예제에서는 활성화된 추적 수준에 대한 정보를 콘솔에 기록합니다. 구성 파일에서는 SyncTracer 스위치에 값 3을 지정합니다. 이 값은 Info에 해당하므로 Error, Warning 및 Info는 True를 반환하고, Verbose는 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())
다음 코드 예제에서는 데이터 충돌에 대한 서식이 지정된 경고 메시지를 추적 출력에 기록하는 방법을 보여 줍니다. 충돌에 대한 자세한 내용은 방법: 데이터베이스 동기화의 데이터 충돌 및 오류 처리(SQL Server)를 참조하십시오. 자세한 정보 표시 추적에는 충돌에 대한 정보가 포함됩니다. 이 응용 프로그램에서는 자세한 정보 표시 추적이 비활성화되어 있으며 응용 프로그램에서는 충돌을 경고로 표시합니다.
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
활성화된 추적 수준을 확인하면 비용이 많이 들 수 있는 처리를 방지할 수 있습니다. 예제 코드에서는 자세한 정보 표시 추적이 이미 활성화된 경우 응용 프로그램에서 추가 처리를 방지합니다. 이와 반대로, 특정 추적 수준이 활성화된 경우에만 응용 프로그램에서 출력을 생성할 수도 있습니다.
추적의 보안 고려 사항
추적 파일에는 서버 및 클라이언트 컴퓨터, 응용 프로그램 데이터, 로그인에 대한 정보가 포함될 수 있습니다. 암호는 추적 파일에 기록되지 않습니다. 자세한 정보 표시 추적이 활성화되어 있으면 데이터베이스에서 변경된 각 행이 추적 파일에 기록됩니다. 적절한 액세스 제어 목록을 사용하여 추적 파일을 보호해야 합니다.
전체 코드 예제
다음의 전체 코드 예제에는 이 항목 앞부분에서 설명한 코드 예제와 동기화를 수행하는 데 필요한 추가 코드가 포함되어 있습니다. 응용 프로그램을 실행하기 전에 다음 단계를 수행하십시오.
Visual Studio에서 프로젝트를 만듭니다.
데이터베이스 공급자용 유틸리티 클래스 방법 항목에서 사용할 수 있는 Utility 클래스와 Sync Framework DLL에 대한 참조를 추가합니다.
구성 파일을 만들고 이 항목 앞부분에 나와 있는 예제에서 XML 코드를 복사합니다.
Utility 클래스의 메서드 호출에 유의해야 합니다.
Utility.MakeFailingChangeOnNode(Utility.ConnStr_SqlSync_Client) 서버에서 적용될 때 실패하는 변경을 클라이언트에서 수행합니다. 제약 조건 위반 및 관련 응용 프로그램 예외가 자동으로 추적 파일에 경고로 기록됩니다.
Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Client, "Customer") 및 Utility.MakeConflictingChangeOnNode(Utility.ConnStr_SqlSync_Server, "Customer"). 동기화될 때 충돌하는 변경을 클라이언트와 서버에서 수행합니다. 충돌은 SampleSyncProvider_ApplyChangeFailed 이벤트 처리기에서 추적 파일에 기록됩니다.
응용 프로그램을 실행한 후 추적 출력 파일을 열고 자동으로 기록된 메시지와 응용 프로그램 코드의 결과인 충돌 경고를 확인하십시오.
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