Sunteți pe pagina 1din 9

//WRITING NOTES:

//Modified to use connection string


//Modified to be class independent of forms
//Created DatabaseInfo object
//Settings for SQLServerConnectionString, SQLExpressConnectionString,ScopeName
using
using
using
using
using
using
using
using

System;
System.Collections.Generic;
System.Text;
System.Data.SqlClient;
System.Data.SqlServerCe;
System.Data;
System.IO;
System.Windows.Forms;

using
using
using
using

Microsoft.Synchronization;
Microsoft.Synchronization.Data;
Microsoft.Synchronization.Data.SqlServer;
Microsoft.Synchronization.Data.SqlServerCe;

namespace SyncLibrary
{
public class SynchronizationHelper
{
DatabaseSyncInfo dbInfo;
ListBox ProgressDisplay = null;
public SynchronizationHelper(DatabaseSyncInfo dbInfo)
{
this.dbInfo = dbInfo;
}
/// <summary>
/// Utility function that will create a SyncOrchestrator and synchronize the two passed in
providers
/// </summary>
/// <param name="localProvider">Local store provider</param>
/// <param name="remoteProvider">Remote store provider</param>
/// <returns></returns>
private SyncOperationStatistics SynchronizeProviders(RelationalSyncProvider localProvider,
RelationalSyncProvider remoteProvider)
{
SyncOrchestrator orchestrator = new SyncOrchestrator();
orchestrator.LocalProvider = localProvider;
orchestrator.RemoteProvider = remoteProvider;
orchestrator.Direction = SyncDirectionOrder.UploadAndDownload;
// Local provider should be SQL Server when sync with SQL Server Express
// Local provider should be SQL Server Express when sync with SQL Server Compact
// Local provider is the reference schema and only the remote may be missing schema
//Check to see if local provider needs schema, based on SQL Server or SQL CE
//Local provider has local sql connection
if (dbInfo.RemoteDriverType == eSyncDriverType.SQLServer)
{

CheckIfSqlServerProviderNeedsSchema(
localProvider as SqlSyncProvider,
new SqlConnection(dbInfo.RemoteConnectionString));

}
else if (dbInfo.RemoteDriverType == eSyncDriverType.SQLCompact)
{
CheckIfSqlServerCeProviderNeedsSchema(
localProvider as SqlSyncProvider,
new SqlCeConnection(dbInfo.RemoteConnectionString));
}
SyncOperationStatistics stats = orchestrator.Synchronize();
return stats;
}
/// <summary>
/// Check to see if the passed in SqlSyncProvider needs Schema from server
/// </summary>
/// <param name="localProvider"></param>
private void CheckIfSqlServerProviderNeedsSchema(SqlSyncProvider localProvider,
SqlConnection remoteSqlConnection)
{
if (localProvider != null)
{
SqlConnection sqlConn = (SqlConnection)localProvider.Connection;
SqlSyncScopeProvisioning sqlConfig = new
SqlSyncScopeProvisioning(sqlConn);
string scopeName = localProvider.ScopeName;
if (!sqlConfig.ScopeExists(scopeName))
{
DbSyncScopeDescription scopeDesc =
SqlSyncDescriptionBuilder.GetDescriptionForScope(dbInfo.ScopeName, remoteSqlConnection);
sqlConfig.PopulateFromScopeDescription(scopeDesc);
sqlConfig.Apply();
}
}
}
/// <summary>
/// Check to see if the passed in SqlCeSyncProvider needs Schema from server
/// </summary>
/// <param name="localProvider"></param>
private void CheckIfSqlServerCeProviderNeedsSchema(SqlSyncProvider localProvider,
SqlCeConnection remoteSqlCeConnection)
{
if (localProvider != null)
{
SqlConnection sqlConn = (SqlConnection)localProvider.Connection;
SqlCeConnection sqlCeConn = remoteSqlCeConnection;
SqlCeSyncScopeProvisioning sqlCeConfig = new
SqlCeSyncScopeProvisioning(sqlCeConn);
string scopeName = localProvider.ScopeName;
if (!sqlCeConfig.ScopeExists(scopeName))
{
DbSyncScopeDescription scopeDesc =
SqlSyncDescriptionBuilder.GetDescriptionForScope(dbInfo.ScopeName, sqlConn);

sqlCeConfig.PopulateFromScopeDescription(scopeDesc);
sqlCeConfig.Apply();
}

}
public bool CheckIfConnectionsAreTheSame(string Connection1, string Connection2)
{
bool result = false;
string connString1 = Connection1.ToLower().Trim();
string connString2 = Connection2.ToLower().Trim();
try
{
if (string.IsNullOrEmpty(connString1) == false && string.IsNullOrEmpty(connString2)
== false) {
SqlConnection conn1 = new SqlConnection(connString1);
SqlConnection conn2 = new SqlConnection(connString2);
if (conn1.Database == conn2.Database && conn1.DataSource ==
conn2.DataSource)
{
result = true;
}
}
}
catch (Exception ex)
{
throw new Exception("Unable to compare SQL connection strings",ex);
}
return result;
}
/// <summary>
/// Configure the SqlSyncprovider. Note that this method assumes you have a direct
connection
/// to the server as this is more of a design time use case vs. runtime use case. We think
/// of provisioning the server as something that occurs before an application is deployed
whereas
/// provisioning the client is somethng that happens during runtime (on intitial sync) after the
/// application is deployed.
///
/// </summary>
/// <param name="hostName"></param>
/// <returns></returns>
private SqlCeSyncProvider ConfigureSqlServerCeSyncProvider(SqlCeConnection
sqlCeConnection)
{
SqlCeSyncProvider provider = new SqlCeSyncProvider();
provider.ScopeName = dbInfo.ScopeName;
provider.Connection = sqlCeConnection;
if (ProgressDisplay != null)
{
//1. Register the BeginSnapshotInitialization event handler. Called when a CE peer
pointing to an uninitialized
// snapshot database is about to being initialization.
provider.BeginSnapshotInitialization += new
EventHandler<DbBeginSnapshotInitializationEventArgs>(provider_BeginSnapshotInitialization);

//2. Register the EndSnapshotInitialization event handler. Called when a CE peer


pointing to an uninitialized
// snapshot database has been initialized for the given scope.
provider.EndSnapshotInitialization += new
EventHandler<DbEndSnapshotInitializationEventArgs>(provider_EndSnapshotInitialization);
//Register the BatchSpooled and BatchApplied events. These are fired when a provider
is either enumerating or applying changes in batches.
provider.BatchApplied += new
EventHandler<DbBatchAppliedEventArgs>(provider_BatchApplied);
provider.BatchSpooled += new
EventHandler<DbBatchSpooledEventArgs>(provider_BatchSpooled);
}
//create a new scope description and add the appropriate tables to this scope
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription(dbInfo.ScopeName);
//class to be used to provision the scope defined above
SqlCeSyncScopeProvisioning serverConfig = new
SqlCeSyncScopeProvisioning((SqlCeConnection)provider.Connection);

//determine if this scope already exists on the server and if not go ahead and provision
if (serverConfig.ScopeExists(dbInfo.ScopeName) == false)
{
SqlConnection sqlConn = new SqlConnection(dbInfo.LocalConnectionString);
foreach (var item in dbInfo.SyncTables)
{
scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable(item,
sqlConn));
}
//note that it is important to call this after the tables have been added to the scope
serverConfig.PopulateFromScopeDescription(scopeDesc);
//indicate that the base table already exists and does not need to be created
serverConfig.SetCreateTableDefault(DbSyncCreationOption.CreateOrUseExisting);

//provision the server


serverConfig.Apply();

return provider;

/// <summary>
/// Create a SqlSyncProvider instance without provisioning its database.
/// </summary>
/// <param name="sqlConnection"></param>
/// <returns></returns>
private SqlSyncProvider ConfigureSqlServerSyncProvider(SqlConnection sqlConnection)
{
SqlSyncProvider provider = new SqlSyncProvider();
provider.ScopeName = dbInfo.ScopeName;
provider.Connection = sqlConnection;

//Register the BatchSpooled and BatchApplied events. These are fired when a provider is
either enumerating or applying changes in batches.
if (ProgressDisplay != null)
{
provider.BatchApplied += new
EventHandler<DbBatchAppliedEventArgs>(provider_BatchApplied);
provider.BatchSpooled += new
EventHandler<DbBatchSpooledEventArgs>(provider_BatchSpooled);
}
//create a new scope description and add the appropriate tables to this scope
DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription(dbInfo.ScopeName);
//class to be used to provision the scope defined above
SqlSyncScopeProvisioning serverConfig = new
SqlSyncScopeProvisioning((SqlConnection)provider.Connection);
//determine if this scope already exists on the server and if not go ahead and provision
if (serverConfig.ScopeExists(dbInfo.ScopeName) == false)
{
SqlConnection remoteConn = new SqlConnection(dbInfo.LocalConnectionString);
foreach (var item in dbInfo.SyncTables)
{
scopeDesc.Tables.Add(SqlSyncDescriptionBuilder.GetDescriptionForTable(item,
remoteConn));
}
//note that it is important to call this after the tables have been added to the scope
serverConfig.PopulateFromScopeDescription(scopeDesc);
//indicate that the base table already exists and does not need to be created
serverConfig.SetCreateTableDefault(DbSyncCreationOption.CreateOrUseExisting);
//provision the server
serverConfig.Apply();
}

//Thats it. We are done configuring the SQL provider.


return provider;

/// <summary>
/// Called whenever an enumerating provider spools a batch file to the disk
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void provider_BatchSpooled(object sender, DbBatchSpooledEventArgs e)
{
if (ProgressDisplay != null)
{
this.ProgressDisplay.Items.Add("BatchSpooled event fired: Details");
this.ProgressDisplay.Items.Add("\tSource Database :" +
((RelationalSyncProvider)sender).Connection.Database);
this.ProgressDisplay.Items.Add("\tBatch Name
:" + e.BatchFileName);
this.ProgressDisplay.Items.Add("\tBatch Size
:" + e.DataCacheSize);
this.ProgressDisplay.Items.Add("\tBatch Number :" + e.CurrentBatchNumber);
this.ProgressDisplay.Items.Add("\tTotal Batches :" + e.TotalBatchesSpooled);

this.ProgressDisplay.Items.Add("\tBatch Watermark :" +


ReadTableWatermarks(e.CurrentBatchTableWatermarks));
}
}
/// <summary>
/// Calls when the destination provider successfully applies a batch file.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void provider_BatchApplied(object sender, DbBatchAppliedEventArgs e)
{
if (ProgressDisplay != null)
{
this.ProgressDisplay.Items.Add("BatchApplied event fired: Details");
this.ProgressDisplay.Items.Add("\tDestination Database :" +
((RelationalSyncProvider)sender).Connection.Database);
this.ProgressDisplay.Items.Add("\tBatch Number
:" + e.CurrentBatchNumber);
this.ProgressDisplay.Items.Add("\tTotal Batches To Apply :" + e.TotalBatchesToApply);
}
}
/// <summary>
/// Reads the watermarks for each table from the batch spooled event. Denotes the max
tickcount for each table in each batch
/// </summary>
/// <param name="dictionary">Watermark dictionary retrieved from
DbBatchSpooledEventArgs</param>
/// <returns>String</returns>
private string ReadTableWatermarks(Dictionary<string, ulong> dictionary)
{
StringBuilder builder = new StringBuilder();
Dictionary<string, ulong> dictionaryClone = new Dictionary<string, ulong>(dictionary);
foreach (KeyValuePair<string, ulong> kvp in dictionaryClone)
{
builder.Append(kvp.Key).Append(":").Append(kvp.Value).Append(",");
}
return builder.ToString();
}
#region Static Helper Functions
/// <summary>
/// A utility function to check if a database with the same name exists.
/// </summary>
/// <param name="dbName"></param>
/// <param name="conn"></param>
/// <returns></returns>
private static bool DatabaseExists(string dbName, SqlConnection conn)
{
SqlCommand command = new SqlCommand(
string.Format("SELECT name FROM SYS.DATABASES WHERE name = N'{0}'",
dbName), conn);
try
{
conn.Open();
SqlDataAdapter sqlAdapter = new SqlDataAdapter(command);

DataSet dataSet = new DataSet();


sqlAdapter.Fill(dataSet);
if (dataSet.Tables[0].Rows.Count > 0)
{
// Database with the given name exists
//
return true;
}

}
finally
{
if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
}
return false;
}
public SyncOperationStatistics DoSync(ListBox optionalProgressDisplay)
{
if (optionalProgressDisplay != null) {
this.ProgressDisplay = optionalProgressDisplay;
}
SqlSyncProvider localProvider = ConfigureSqlServerSyncProvider(new
SqlConnection(this.dbInfo.LocalConnectionString));
RelationalSyncProvider remoteProvider = null;
if (dbInfo.RemoteDriverType == eSyncDriverType.SQLServer)
{
remoteProvider = ConfigureSqlServerSyncProvider(new
SqlConnection(this.dbInfo.RemoteConnectionString));
}
else if (dbInfo.RemoteDriverType == eSyncDriverType.SQLCompact)
{
remoteProvider = ConfigureSqlServerCeSyncProvider(new
SqlCeConnection(this.dbInfo.RemoteConnectionString));
}
//Set memory data cache size property. 0 represents non batched mode
localProvider.MemoryDataCacheSize = dbInfo.LocalBatchSize;
remoteProvider.MemoryDataCacheSize = dbInfo.RemoteBatchSize;
//Set batch spool location. Default value if not set is %Temp% directory.
if (string.IsNullOrEmpty(dbInfo.LocalBatchSpoolFolder) == false)
{
localProvider.BatchingDirectory = dbInfo.LocalBatchSpoolFolder;
}
if (string.IsNullOrEmpty(dbInfo.RemoteBatchSpoolFolder) == false)
{
remoteProvider.BatchingDirectory = dbInfo.RemoteBatchSpoolFolder;
}
SyncOperationStatistics stats = this.SynchronizeProviders(localProvider, remoteProvider);

//TimeSpan diff = stats.SyncEndTime.Subtract(stats.SyncStartTime);


//Print Sync stats object
//this.syncStats.Text = string.Format("Batching: {4} - Total Time To Synchronize = {0}:
{1}:{2}:{3}",
// diff.Hours, diff.Minutes, diff.Seconds, diff.Milliseconds, (this._batchSize > 0) ?
"Enabled" : "Disabled");
//this.ReadTableValuesForSelectedTab();
return stats;
}
/// <summary>
/// Snapshot intialization process completed. Database is now ready for sync with other
existing peers in topology
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void provider_EndSnapshotInitialization(object sender,
DbEndSnapshotInitializationEventArgs e)
{
if (ProgressDisplay != null)
{
this.ProgressDisplay.Items.Add("EndSnapshotInitialization Event fired.");
ShowSnapshotInitializationStatistics(e.InitializationStatistics,
e.TableInitializationStatistics);
this.ProgressDisplay.Items.Add("Snapshot Initialization Process Completed.....");
}
}
/// <summary>
/// CE provider detected that the database was imported from a snapshot from another peer.
Snapshot initialziation about to begin
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void provider_BeginSnapshotInitialization(object sender,
DbBeginSnapshotInitializationEventArgs e)
{
if (ProgressDisplay != null)
{
this.ProgressDisplay.Items.Add("Snapshot Initialization Process Started.....");
this.ProgressDisplay.Items.Add(string.Format("BeginSnapshotInitialization Event fired
for Scope {0}", e.ScopeName));
}
}
//Show DbSnapshotInitializationStatistics object
private void ShowSnapshotInitializationStatistics(DbSnapshotInitializationStatistics
snapshotStats, Dictionary<string, DbSnapshotInitializationTableStatistics> tableStats)
{
string message = "";
this.ProgressDisplay.Items.Add(message);
message = "-----Snapshot Initialization Statistics -----";
this.ProgressDisplay.Items.Add(message);
message = "Total # of tables: " + snapshotStats.TotalTables;
this.ProgressDisplay.Items.Add(message);
message = "Tables Initialized: " + snapshotStats.TablesInitialized;
this.ProgressDisplay.Items.Add(message);

message = "Start Time: " + snapshotStats.StartTime;


this.ProgressDisplay.Items.Add(message);
message = "End Time: " + snapshotStats.EndTime;
this.ProgressDisplay.Items.Add(message);
message = "\t-----Individual Snapshot Table Statistics-----";
this.ProgressDisplay.Items.Add(message);
foreach (string tableName in tableStats.Keys)
{
DbSnapshotInitializationTableStatistics ts = tableStats[tableName];
message = "\tTable Name: " + tableName;
this.ProgressDisplay.Items.Add(message);
message = "\tTotal Rows: " + ts.TotalRows;
this.ProgressDisplay.Items.Add(message);
message = "\tRows Intialized: " + ts.RowsInitialized;
this.ProgressDisplay.Items.Add(message);
message = "\tStart Time: " + ts.StartTime;
this.ProgressDisplay.Items.Add(message);
message = "\tEnd Time: " + ts.EndTime;
this.ProgressDisplay.Items.Add(message);
}
Application.DoEvents();
}
}
}

#endregion

S-ar putea să vă placă și