項目應用場景:
某客戶是一個大型集團企業的信息部門,掌管着企業幾百臺服務器,並且以後會不斷擴充;
爲了更好的維護信息辦的服務器;信息部門需要開發一套維護系統,來記錄各個服務器的相關狀態信息(如,IP、所安裝在服務器的應用系統和相關信息等),便於維護和查詢;客戶維護人員可以攜帶筆記本脫機即時編寫維修服務器相關記錄信息(增、刪、查、改),客戶端聯網即可自動同步到服務器最新維護數據。
客戶要求實現服務器端和客戶端雙向同步的方式,客戶端操作數據服務器端自動更新,反之操作服務器端,
爲了更好的維護信息辦的服務器;信息部門需要開發一套維護系統,來記錄各個服務器的相關狀態信息(如,IP、所安裝在服務器的應用系統和相關信息等),便於維護和查詢;客戶維護人員可以攜帶筆記本脫機即時編寫維修服務器相關記錄信息(增、刪、查、改),客戶端聯網即可自動同步到服務器最新維護數據。
客戶要求實現服務器端和客戶端雙向同步的方式,客戶端操作數據服務器端自動更新,反之操作服務器端,
客戶端自動更新。
早先的解決方案:
客戶端和服務端都使用SQL2005數據庫,並採用數據庫訂閱的方式進行數據同步。
此種設計缺點是:
1、客戶端必須安裝SQL2005(包括SQL Server Express),造成操作客戶端系統運行速度降低,佔用客戶端計算機系統資源大等問題。
2、同步方式配置複雜。需要訂閱方式發佈服務器和訂閱服務器互相註冊,服務器端與客戶端配置複雜。
此種設計缺點是:
1、客戶端必須安裝SQL2005(包括SQL Server Express),造成操作客戶端系統運行速度降低,佔用客戶端計算機系統資源大等問題。
2、同步方式配置複雜。需要訂閱方式發佈服務器和訂閱服務器互相註冊,服務器端與客戶端配置複雜。
最新的解決方案:
1、客戶端數據庫採用微軟Microsoft SQL Server Compact 3.5壓縮數據庫,實施技術Microsoft Synchronization Services v1.0。
2、服務器端數據庫採用微軟Microsoft SQL Server 2005(2008)。
3、開發工具是Microsoft Visual Studio 2008正式版。
2、服務器端數據庫採用微軟Microsoft SQL Server 2005(2008)。
3、開發工具是Microsoft Visual Studio 2008正式版。
服務器端開發B/S維護系統;C/S端開發客戶端Winform(WPF)應用程序;實現客戶端winform系統業務操作會相應的同步到服務器中;反之,服務器端的業務系統操作也會自動同步到下屬不同的客戶端Compact數據庫中。
架構設計:
1、服務器端系統架構設計採用MVPC架構:Web Client Software Factory
2、客戶端業務系統架構設計也MVPC架構:Smart Client Software Factory
優點:客戶端模塊代碼很容易移植到服務器端asp.net開發的業務系統;解決客戶端與服務器端應用模塊重複,減少代碼量;
如果採用Microsoft SQL Server Compact 3.5客戶端數據庫(Sdf擴展名的數據庫文件),不必安裝Microsoft SQL Server 2005數據庫,客戶只需要一個安裝包,即可實現客戶端的應用系統安裝,更加方便靈活的用戶體驗。
如果採用Microsoft SQL Server Compact 3.5客戶端數據庫(Sdf擴展名的數據庫文件),不必安裝Microsoft SQL Server 2005數據庫,客戶只需要一個安裝包,即可實現客戶端的應用系統安裝,更加方便靈活的用戶體驗。
Sync Services for ADO.NET provider 技術應用場景
* C\S結構的離線應用,在本地緩存中心數據庫中的部分數據(極特別的情況下會緩存全部)。應用程序使用緩存的數據,並在特定的時間把一批更改上次到中心數據庫。
* C\S結構的離線應用,在本地緩存中心數據庫中的部分數據(極特別的情況下會緩存全部)。應用程序使用緩存的數據,並在特定的時間把一批更改上次到中心數據庫。
* 協作應用,應用程序只使用本地數據,並週期性的與其他參與者進行 Peer-to-Peer 的同步。數據庫雙向同步:Feature實現客戶端C/S系統數據庫數據與服務器端B/S管理系統數據庫數據進行數據雙向同步;
Use case:用戶在客戶端針對本地數據庫系統進行增加、刪除、修改等操作,如果在線聯網狀態,數據自動同步到遠程SQL Server數據庫;有管理權限用戶,登錄B/S系統進行增加、刪除、修改等操作,SQL Server數據自動同步到客戶端數據庫。
功能實現Server與Client端進行數據操作(增加、刪除),都能很好的進行數據雙向同步;
Sync Demo源碼程序截圖如下:
Compact數據庫同步技術功能
Microsoft SQL Server Compact 3.5 (以前稱爲 Microsoft SQL Server 2005 Mobile Edition)是一種壓縮數據庫,很適合嵌入到移動應用程序和桌面應用程序中。Microsoft SQL Server Compact 3.5 爲開發本機和託管應用程序的開發人員提供了與其他 SQL Server 版本通用的編程模型。SQL Server Compact Edition 以很少的空間佔用提供關係數據庫功能:健壯數據存儲、優化查詢處理器以及可靠、可縮放的連接。
Microsoft Synchronization Services for ADO.NET 是一組 DLL,提供了一個可組構的 API。根據應用程序的體系結構和要求,可以使用提供的所有或部分組件。
Synchronization Services 實現了 SQL Server Compact 3.5 客戶端數據庫和服務器數據庫或任何其他數據源(如以 XML 形式提供股票報價的服務)之間的同步。在同步兩個數據庫方面,Synchronization Services 支持使用爲之提供了 ADO.NET 提供程序的任何服務器數據庫的雙層和 N 層體系結構。
在對客戶端數據庫和其他類型的數據源進行同步方面,Synchronization Services 支持基於服務的體系結構。與雙層或 N 層體系結構相比,此體系結構需要編寫更多的應用程序代碼;但是,它不需要開發人員採取另一種不同的方式進行同步。
通過 Microsoft Visual Studio 2008 的Microsoft Synchronization Services for ADO.NET,可以通過雙層、N 層和基於服務的體系結構同步來自不同來源的數據。
在對客戶端數據庫和其他類型的數據源進行同步方面,Synchronization Services 支持基於服務的體系結構。與雙層或 N 層體系結構相比,此體系結構需要編寫更多的應用程序代碼;但是,它不需要開發人員採取另一種不同的方式進行同步。
通過 Microsoft Visual Studio 2008 的Microsoft Synchronization Services for ADO.NET,可以通過雙層、N 層和基於服務的體系結構同步來自不同來源的數據。
Synchronization Services API 提供了一組用於在數據服務和本地存儲區之間同步數據的組件,而不是僅僅用於複製數據庫及其架構。應用程序正越來越多地用於移動客戶端,如便攜式計算機和設備。由於這些移動客戶端與中央服務器沒有連貫或可靠的網絡連接,因此對於這些應用程序而言,能夠在客戶端上使用數據的一份本地副本十分重要。同等重要的一點是:在網絡連接可用時,需要能夠將數據的本地副本與中央服務器同步。Synchronization Services API 以 ADO.NET 數據訪問 API 爲藍本,提供了一種直觀的數據同步手段。Synchronization Services 對構建依靠連續網絡連接的應用程序這一工作進行了合乎邏輯的擴展,使我們得以針對斷續連接的網絡環境開發應用程序。
相關代碼:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServerCe;
using Microsoft.Synchronization.Data.Server;
using System.Data.SqlServerCe;
namespace SynchrnoizationDemo
{
public partial class frmMain : Form
{
#region 定義常規數據區
private const string strServerListTableName = "ServerList";
private const string strUserListTableName = "UserList";
private const string strCreationTrackingColumn = @"create_timestamp";
private const string strUpdateTrackingColumn = @"update_timestamp";
private const string strDeletionTrackingColumn = @"update_timestamp";
private const string strUpdateOriginatorIdColumn = @"update_originator_id";
private const string strNoClientDBWarnString = "客戶端數據庫不存在,請首先執行 雙向同步 命令";
#endregion
#region 定義變量區
private string strServerIP = @"(local)";
private string strServerDataBaseName = "SyncDemo";
private string strLoginUser = "sa";
private string strLoginPassWord = "sa";
private string strServerConnectString = "";
private string strClientDataBasename = Application.StartupPath + @"\ClientDB.sdf";
private string strClientConnectString = "";
private bool blnOperationClient = true;
private string strOperationTable = strServerListTableName;
#endregion
public frmMain()
{
InitializeComponent();
}
private void frmMain_Load(object sender, EventArgs e)
{
// 初始化相關參數
SetEnvironmentValue();
}
#region 通用方法
/// <summary>
/// 獲取相關參數
/// </summary>
private void GetEnvironmentValue()
{
strServerIP = txtServerIP.Text.Trim();
strServerDataBaseName = txtServerDataBaseName.Text.Trim();
strLoginUser = txtLoginUser.Text.Trim();
strLoginPassWord = txtLoginPassWord.Text.Trim();
strServerConnectString = "Data Source="+strServerIP+";Initial Catalog="+strServerDataBaseName+";User ID="+strLoginUser+";Password="+strLoginPassWord+";";
strClientDataBasename = txtClientDataBaseFileName.Text.Trim();
strClientConnectString = "Data Source=" + strClientDataBasename;
blnOperationClient = rbClient.Checked;
strOperationTable = tabMain.SelectedIndex == 0 ? strServerListTableName : strUserListTableName;
}
/// <summary>
/// 顯示相關參數
/// </summary>
private void SetEnvironmentValue()
{
txtServerIP.Text = strServerIP;
txtServerDataBaseName.Text = strServerDataBaseName;
txtLoginUser.Text = strLoginUser;
txtLoginPassWord.Text = strLoginPassWord;
txtClientDataBaseFileName.Text = strClientDataBasename;
if (blnOperationClient)
{
rbClient.Checked=true;
rbServer.Checked=false;
}
else
{
rbServer.Checked=true;
rbClient.Checked=false;
}
}
/// <summary>
/// 檢查客戶端數據庫是否存在
/// </summary>
/// <returns></returns>
private Boolean CheckClientDb()
{
//這裏默認已經調用了GetEnvironmentValue函數
return File.Exists(strClientDataBasename);
}
#endregion
#region 刷新數據
/// <summary>
/// 刷新調用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btRefersh_Click(object sender, EventArgs e)
{
btRefersh.Enabled = false;
//RefreshData(tabMain.SelectedTab.Text.Replace("表", ""));
RefreshData(strServerListTableName);
RefreshData(strUserListTableName);
btRefersh.Enabled = true;
}
/// <summary>
/// 數據刷新
/// </summary>
/// <param name="sTableName"></param>
private void RefreshData(string sTableName)
{
GetEnvironmentValue();
string sSql = "Select * from " + sTableName;
DataTable dtQuery;
try
{
#region 讀取服務器端
SqlDataAdapter serverLoadAdapter = new SqlDataAdapter(sSql, strServerConnectString);
dtQuery = new DataTable();
serverLoadAdapter.Fill(dtQuery);
RemoveServerTrackingColumns(dtQuery);
switch (sTableName)
{
case strServerListTableName:
dgSLServer.DataSource = dtQuery;
break;
case strUserListTableName:
dgULServer.DataSource = dtQuery;
break;
}
#endregion
#region 讀取客戶端
if (CheckClientDb())
{
SqlCeDataAdapter clientLoadAdapter = new SqlCeDataAdapter(sSql,strClientConnectString);
dtQuery = new DataTable();
clientLoadAdapter.Fill(dtQuery);
RemoveClientTrackingColumns(dtQuery);
switch (sTableName)
{
case strServerListTableName:
dgSLClient.DataSource = dtQuery;
break;
case strUserListTableName:
dgULClient.DataSource = dtQuery;
break;
}
}
else
{
MessageBox.Show(strNoClientDBWarnString);
}
#endregion
}
catch (System.Exception e)
{
MessageBox.Show(e.Message);
}
}
/// <summary>
/// 去除客戶端表中不需要顯示的列
/// </summary>
/// <param name="dataTable"></param>
private static void RemoveClientTrackingColumns(DataTable dataTable)
{
if (dataTable.Columns.Contains("__sysInsertTxBsn"))
{
dataTable.Columns.Remove("__sysInsertTxBsn");
}
if (dataTable.Columns.Contains("__sysChangeTxBsn"))
{
dataTable.Columns.Remove("__sysChangeTxBsn");
}
}
/// <summary>
/// 去除服務器端表中不需要顯示的列
/// </summary>
/// <param name="dataTable"></param>
private static void RemoveServerTrackingColumns(DataTable dataTable)
{
if (dataTable.Columns.Contains("update_timestamp"))
{
dataTable.Columns.Remove("update_timestamp");
}
if (dataTable.Columns.Contains("create_timestamp"))
{
dataTable.Columns.Remove("create_timestamp");
}
if (dataTable.Columns.Contains("update_originator_id"))
{
dataTable.Columns.Remove("update_originator_id");
}
}
#endregion
#region 同步數據
/// <summary>
/// 同步數據調用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btSync_Click(object sender, EventArgs e)
{
btSync.Enabled = false;
panOperation.Enabled = false;
SynchronizeData();
btRefersh_Click(null, null);
panOperation.Enabled = true;
btSync.Enabled = true;
}
/// <summary>
/// 數據同步
/// </summary>
private void SynchronizeData()
{
#region 初始化
GetEnvironmentValue();
SyncAgent syncAgent = new SyncAgent();
#endregion
try
{
#region 服務器端準備
DbServerSyncProvider serverSyncProvider = new DbServerSyncProvider();
SqlConnection serverConnection = new SqlConnection(strServerConnectString);
serverSyncProvider.Connection = serverConnection;
syncAgent.RemoteProvider = serverSyncProvider;
#endregion
#region 客戶端準備
if (!CheckClientDb())
{
SqlCeEngine clientEngine = new SqlCeEngine(strClientConnectString);
clientEngine.CreateDatabase();
clientEngine.Dispose();
}
SqlCeClientSyncProvider clientSyncProvider = new SqlCeClientSyncProvider(strClientConnectString);
syncAgent.LocalProvider = clientSyncProvider;
#endregion
#region SyncTable和SyncGroup準備
// ServerList表
SyncTable tableServerList = new SyncTable(strServerListTableName);
tableServerList.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
tableServerList.SyncDirection = SyncDirection.Bidirectional;
// UserList表
SyncTable tableUserList = new SyncTable(strUserListTableName);
tableUserList.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
tableUserList.SyncDirection = SyncDirection.Bidirectional;
// SyncGroup
SyncGroup syncGroup = new SyncGroup("SyncDemo");
tableServerList.SyncGroup = syncGroup;
tableUserList.SyncGroup = syncGroup;
syncAgent.Configuration.SyncTables.Add(tableServerList);
syncAgent.Configuration.SyncTables.Add(tableUserList);
#endregion
#region SyncAdapter準備
#region ServerList準備
SqlSyncAdapterBuilder ServerListBuilder = new SqlSyncAdapterBuilder();
ServerListBuilder.Connection = serverConnection;
ServerListBuilder.SyncDirection = SyncDirection.Bidirectional;
// 主表及其相關列
ServerListBuilder.TableName = strServerListTableName;
ServerListBuilder.DataColumns.Add("ServerID");
ServerListBuilder.DataColumns.Add("ServerIP");
ServerListBuilder.DataColumns.Add("ServerBuyTime");
// tombstone表及其相關列
ServerListBuilder.TombstoneTableName = strServerListTableName + "_tombstone";
ServerListBuilder.TombstoneDataColumns.Add("ServerID");
ServerListBuilder.TombstoneDataColumns.Add("ServerIP");
ServerListBuilder.TombstoneDataColumns.Add("ServerBuyTime");
// 相關的跟蹤列
ServerListBuilder.CreationTrackingColumn = strCreationTrackingColumn;
ServerListBuilder.UpdateTrackingColumn = strUpdateTrackingColumn;
ServerListBuilder.DeletionTrackingColumn = strDeletionTrackingColumn;
ServerListBuilder.UpdateOriginatorIdColumn = strUpdateOriginatorIdColumn;
SyncAdapter ServerListSyncAdapter = ServerListBuilder.ToSyncAdapter();
((SqlParameter)ServerListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_last_received_anchor"]).DbType = DbType.Binary;
((SqlParameter)ServerListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_new_received_anchor"]).DbType = DbType.Binary;
serverSyncProvider.SyncAdapters.Add(ServerListSyncAdapter);
#endregion
#region UserList準備
SqlSyncAdapterBuilder UserListBuilder = new SqlSyncAdapterBuilder();
UserListBuilder.SyncDirection = SyncDirection.Bidirectional;
UserListBuilder.Connection = serverConnection;
// 主表及其相關列
UserListBuilder.TableName = strUserListTableName;
UserListBuilder.DataColumns.Add("UserID");
UserListBuilder.DataColumns.Add("UserName");
UserListBuilder.DataColumns.Add("UserPW");
// tombstone表及其相關列
UserListBuilder.TombstoneTableName = strUserListTableName + "_tombstone";
UserListBuilder.TombstoneDataColumns.Add("UserID");
UserListBuilder.TombstoneDataColumns.Add("UserName");
UserListBuilder.TombstoneDataColumns.Add("UserPW");
// 相關的跟蹤列
UserListBuilder.CreationTrackingColumn = strCreationTrackingColumn;
UserListBuilder.UpdateTrackingColumn = strUpdateTrackingColumn;
UserListBuilder.DeletionTrackingColumn = strDeletionTrackingColumn;
UserListBuilder.UpdateOriginatorIdColumn = strUpdateOriginatorIdColumn;
SyncAdapter UserListSyncAdapter = UserListBuilder.ToSyncAdapter();
((SqlParameter)UserListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_last_received_anchor"]).DbType = DbType.Binary;
((SqlParameter)UserListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_new_received_anchor"]).DbType = DbType.Binary;
serverSyncProvider.SyncAdapters.Add(UserListSyncAdapter);
#endregion
#endregion
#region 數據同步
SqlCommand anchorCmd = new SqlCommand();
anchorCmd.CommandType = CommandType.Text;
anchorCmd.CommandText = "Select @" + SyncSession.SyncNewReceivedAnchor + "= @@DBTS";// " = @@DBTS";
anchorCmd.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp).Direction = ParameterDirection.Output;
serverSyncProvider.SelectNewAnchorCommand = anchorCmd;
SqlCommand clientIdCmd = new SqlCommand();
clientIdCmd.CommandType = CommandType.Text;
clientIdCmd.CommandText = "SELECT @" + SyncSession.SyncOriginatorId + " = 1";
clientIdCmd.Parameters.Add("@" + SyncSession.SyncOriginatorId, SqlDbType.Int).Direction = ParameterDirection.Output;
serverSyncProvider.SelectClientIdCommand = clientIdCmd;
syncAgent.Synchronize();
#endregion
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
#endregion
#region 增量操作
#region 產生各種隨機數據
/// <summary>
/// 產生隨機IP
/// </summary>
/// <returns></returns>
private string ReturnRandomIP(Random rand)
{
string sRe = "192.168." + rand.Next(0, 256).ToString() + "." + rand.Next(0, 256).ToString();
return sRe;
}
/// <summary>
/// 產生隨機ID
/// </summary>
/// <param name="rand"></param>
/// <returns></returns>
private string ReturnRandomID(Random rand)
{
return rand.Next((int)(DateTime.Now.ToFileTime() % 10000)).ToString();
}
/// <summary>
/// 產生隨機字符
/// </summary>
/// <param name="rand"></param>
/// <returns></returns>
private char ReturnRandomChar(Random rand)
{
int ret = rand.Next(122);
while (ret < 48 || (ret > 57 && ret < 65) || (ret > 90 && ret < 97))
{
ret = rand.Next(122);
}
return (char)ret;
}
/// <summary>
/// 產生隨機字符串
/// </summary>
/// <param name="rand"></param>
/// <returns></returns>
private string ReturnRandomString(Random rand)
{
int length = 10;
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
{
sb.Append(ReturnRandomChar(rand));
}
return sb.ToString();
}
#endregion
/// <summary>
/// 返回一個隨機操作的ID
/// </summary>
private string ReturnRandomUpdateOrDeleteID(Random rand)
{
DataTable dtRef = new DataTable();
if (strOperationTable == strServerListTableName)
{
if (blnOperationClient)
{
dtRef = (DataTable)dgSLClient.DataSource;
}
else
{
dtRef = (DataTable)dgSLServer.DataSource;
}
}
else
{
if (blnOperationClient)
{
dtRef = (DataTable)dgULClient.DataSource;
}
else
{
dtRef = (DataTable)dgULServer.DataSource;
}
}
int iTotalRows = dtRef.Rows.Count;
return dtRef.Rows[rand.Next(0, iTotalRows)][0].ToString();
}
/// <summary>
/// 具體執行增刪改的操作
/// </summary>
/// <param name="CommandString"></param>
private void ExecuteOperation(string CommandString)
{
if (blnOperationClient)
{
#region 客戶端操作
if (CheckClientDb())
{
SqlCeConnection conn = new SqlCeConnection(strClientConnectString);
SqlCeCommand cmd = new SqlCeCommand();
cmd.Connection = conn;
cmd.CommandText = CommandString.ToString();
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (System.Exception exp)
{
MessageBox.Show(exp.Message);
}
finally
{
conn.Close();
}
}
else
{
MessageBox.Show(strNoClientDBWarnString);
}
#endregion
}
else
{
#region 服務器端操作
SqlConnection conn = new SqlConnection(strServerConnectString);
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = CommandString;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (System.Exception exp)
{
MessageBox.Show(exp.Message);
}
finally
{
conn.Close();
}
#endregion
}
RefreshData(strOperationTable);
}
/// <summary>
/// 更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btUpdate_Click(object sender, EventArgs e)
{
btUpdate.Enabled = false;
#region 初始化
GetEnvironmentValue();
Random rand = new Random();
StringBuilder sSql = new StringBuilder();
if (strOperationTable == strServerListTableName)
{
//update ServerList set ServerIP='123.12',ServerBuyTime=GetDate() where ServerID=1
sSql.Append(@" update ServerList ");
sSql.Append(@" set ServerIP='"+ReturnRandomIP(rand)+"', "); //ServerIP
sSql.Append(@" ServerBuyTime=GetDate() "); //ServerBuyTime
sSql.Append(@" where ServerID="+ReturnRandomUpdateOrDeleteID(rand)+" "); //UpdateID
}
else
{
sSql.Append(@" update UserList ");
sSql.Append(@" set UserName = '"+ReturnRandomString(rand)+"', "); //UserName
sSql.Append(@" UserPW = '"+ReturnRandomString(rand)+"' "); //UserPW
sSql.Append(@" where UserID="+ReturnRandomUpdateOrDeleteID(rand)+" "); //UpdateID
}
#endregion
ExecuteOperation(sSql.ToString());
btUpdate.Enabled = true;
}
/// <summary>
/// 添加
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btAdd_Click(object sender, EventArgs e)
{
btAdd.Enabled = false;
#region 初始化
GetEnvironmentValue();
Random rand = new Random();
StringBuilder sSql = new StringBuilder();
if (strOperationTable == strServerListTableName)
{
sSql.Append(@" insert into ServerList (ServerID, ServerIP, ServerBuyTime) ");
sSql.Append(@" values( ");
sSql.Append(@" " + ReturnRandomID(rand) + ", "); //ServerID
sSql.Append(@" '" + ReturnRandomIP(rand) + "', "); //ServerIP
sSql.Append(@" GetDate() "); //ServerBuyTime
sSql.Append(@" ) ");
}
else
{
sSql.Append(@" insert into UserList (UserID, UserName, UserPW) ");
sSql.Append(@" values( ");
sSql.Append(@" " + ReturnRandomID(rand) + ", "); //UserID
sSql.Append(@" '" + ReturnRandomString(rand) + "', "); //UserName
sSql.Append(@" '" + ReturnRandomString(rand) + "' "); //UserPW
sSql.Append(@" ) ");
}
#endregion
ExecuteOperation(sSql.ToString());
btAdd.Enabled = true;
}
/// <summary>
/// 刪除
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btDelete_Click(object sender, EventArgs e)
{
btDelete.Enabled = false;
#region 初始化
GetEnvironmentValue();
Random rand = new Random();
StringBuilder sSql = new StringBuilder();
if (strOperationTable == strServerListTableName)
{
sSql.Append(@"delete from ServerList where ServerID="+ReturnRandomUpdateOrDeleteID(rand));
}
else
{
sSql.Append(@"delete from UserList where UserID="+ReturnRandomUpdateOrDeleteID(rand));
}
#endregion
ExecuteOperation(sSql.ToString());
btDelete.Enabled = true;
}
#endregion
}
}
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServerCe;
using Microsoft.Synchronization.Data.Server;
using System.Data.SqlServerCe;
namespace SynchrnoizationDemo
{
public partial class frmMain : Form
{
#region 定義常規數據區
private const string strServerListTableName = "ServerList";
private const string strUserListTableName = "UserList";
private const string strCreationTrackingColumn = @"create_timestamp";
private const string strUpdateTrackingColumn = @"update_timestamp";
private const string strDeletionTrackingColumn = @"update_timestamp";
private const string strUpdateOriginatorIdColumn = @"update_originator_id";
private const string strNoClientDBWarnString = "客戶端數據庫不存在,請首先執行 雙向同步 命令";
#endregion
#region 定義變量區
private string strServerIP = @"(local)";
private string strServerDataBaseName = "SyncDemo";
private string strLoginUser = "sa";
private string strLoginPassWord = "sa";
private string strServerConnectString = "";
private string strClientDataBasename = Application.StartupPath + @"\ClientDB.sdf";
private string strClientConnectString = "";
private bool blnOperationClient = true;
private string strOperationTable = strServerListTableName;
#endregion
public frmMain()
{
InitializeComponent();
}
private void frmMain_Load(object sender, EventArgs e)
{
// 初始化相關參數
SetEnvironmentValue();
}
#region 通用方法
/// <summary>
/// 獲取相關參數
/// </summary>
private void GetEnvironmentValue()
{
strServerIP = txtServerIP.Text.Trim();
strServerDataBaseName = txtServerDataBaseName.Text.Trim();
strLoginUser = txtLoginUser.Text.Trim();
strLoginPassWord = txtLoginPassWord.Text.Trim();
strServerConnectString = "Data Source="+strServerIP+";Initial Catalog="+strServerDataBaseName+";User ID="+strLoginUser+";Password="+strLoginPassWord+";";
strClientDataBasename = txtClientDataBaseFileName.Text.Trim();
strClientConnectString = "Data Source=" + strClientDataBasename;
blnOperationClient = rbClient.Checked;
strOperationTable = tabMain.SelectedIndex == 0 ? strServerListTableName : strUserListTableName;
}
/// <summary>
/// 顯示相關參數
/// </summary>
private void SetEnvironmentValue()
{
txtServerIP.Text = strServerIP;
txtServerDataBaseName.Text = strServerDataBaseName;
txtLoginUser.Text = strLoginUser;
txtLoginPassWord.Text = strLoginPassWord;
txtClientDataBaseFileName.Text = strClientDataBasename;
if (blnOperationClient)
{
rbClient.Checked=true;
rbServer.Checked=false;
}
else
{
rbServer.Checked=true;
rbClient.Checked=false;
}
}
/// <summary>
/// 檢查客戶端數據庫是否存在
/// </summary>
/// <returns></returns>
private Boolean CheckClientDb()
{
//這裏默認已經調用了GetEnvironmentValue函數
return File.Exists(strClientDataBasename);
}
#endregion
#region 刷新數據
/// <summary>
/// 刷新調用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btRefersh_Click(object sender, EventArgs e)
{
btRefersh.Enabled = false;
//RefreshData(tabMain.SelectedTab.Text.Replace("表", ""));
RefreshData(strServerListTableName);
RefreshData(strUserListTableName);
btRefersh.Enabled = true;
}
/// <summary>
/// 數據刷新
/// </summary>
/// <param name="sTableName"></param>
private void RefreshData(string sTableName)
{
GetEnvironmentValue();
string sSql = "Select * from " + sTableName;
DataTable dtQuery;
try
{
#region 讀取服務器端
SqlDataAdapter serverLoadAdapter = new SqlDataAdapter(sSql, strServerConnectString);
dtQuery = new DataTable();
serverLoadAdapter.Fill(dtQuery);
RemoveServerTrackingColumns(dtQuery);
switch (sTableName)
{
case strServerListTableName:
dgSLServer.DataSource = dtQuery;
break;
case strUserListTableName:
dgULServer.DataSource = dtQuery;
break;
}
#endregion
#region 讀取客戶端
if (CheckClientDb())
{
SqlCeDataAdapter clientLoadAdapter = new SqlCeDataAdapter(sSql,strClientConnectString);
dtQuery = new DataTable();
clientLoadAdapter.Fill(dtQuery);
RemoveClientTrackingColumns(dtQuery);
switch (sTableName)
{
case strServerListTableName:
dgSLClient.DataSource = dtQuery;
break;
case strUserListTableName:
dgULClient.DataSource = dtQuery;
break;
}
}
else
{
MessageBox.Show(strNoClientDBWarnString);
}
#endregion
}
catch (System.Exception e)
{
MessageBox.Show(e.Message);
}
}
/// <summary>
/// 去除客戶端表中不需要顯示的列
/// </summary>
/// <param name="dataTable"></param>
private static void RemoveClientTrackingColumns(DataTable dataTable)
{
if (dataTable.Columns.Contains("__sysInsertTxBsn"))
{
dataTable.Columns.Remove("__sysInsertTxBsn");
}
if (dataTable.Columns.Contains("__sysChangeTxBsn"))
{
dataTable.Columns.Remove("__sysChangeTxBsn");
}
}
/// <summary>
/// 去除服務器端表中不需要顯示的列
/// </summary>
/// <param name="dataTable"></param>
private static void RemoveServerTrackingColumns(DataTable dataTable)
{
if (dataTable.Columns.Contains("update_timestamp"))
{
dataTable.Columns.Remove("update_timestamp");
}
if (dataTable.Columns.Contains("create_timestamp"))
{
dataTable.Columns.Remove("create_timestamp");
}
if (dataTable.Columns.Contains("update_originator_id"))
{
dataTable.Columns.Remove("update_originator_id");
}
}
#endregion
#region 同步數據
/// <summary>
/// 同步數據調用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btSync_Click(object sender, EventArgs e)
{
btSync.Enabled = false;
panOperation.Enabled = false;
SynchronizeData();
btRefersh_Click(null, null);
panOperation.Enabled = true;
btSync.Enabled = true;
}
/// <summary>
/// 數據同步
/// </summary>
private void SynchronizeData()
{
#region 初始化
GetEnvironmentValue();
SyncAgent syncAgent = new SyncAgent();
#endregion
try
{
#region 服務器端準備
DbServerSyncProvider serverSyncProvider = new DbServerSyncProvider();
SqlConnection serverConnection = new SqlConnection(strServerConnectString);
serverSyncProvider.Connection = serverConnection;
syncAgent.RemoteProvider = serverSyncProvider;
#endregion
#region 客戶端準備
if (!CheckClientDb())
{
SqlCeEngine clientEngine = new SqlCeEngine(strClientConnectString);
clientEngine.CreateDatabase();
clientEngine.Dispose();
}
SqlCeClientSyncProvider clientSyncProvider = new SqlCeClientSyncProvider(strClientConnectString);
syncAgent.LocalProvider = clientSyncProvider;
#endregion
#region SyncTable和SyncGroup準備
// ServerList表
SyncTable tableServerList = new SyncTable(strServerListTableName);
tableServerList.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
tableServerList.SyncDirection = SyncDirection.Bidirectional;
// UserList表
SyncTable tableUserList = new SyncTable(strUserListTableName);
tableUserList.CreationOption = TableCreationOption.DropExistingOrCreateNewTable;
tableUserList.SyncDirection = SyncDirection.Bidirectional;
// SyncGroup
SyncGroup syncGroup = new SyncGroup("SyncDemo");
tableServerList.SyncGroup = syncGroup;
tableUserList.SyncGroup = syncGroup;
syncAgent.Configuration.SyncTables.Add(tableServerList);
syncAgent.Configuration.SyncTables.Add(tableUserList);
#endregion
#region SyncAdapter準備
#region ServerList準備
SqlSyncAdapterBuilder ServerListBuilder = new SqlSyncAdapterBuilder();
ServerListBuilder.Connection = serverConnection;
ServerListBuilder.SyncDirection = SyncDirection.Bidirectional;
// 主表及其相關列
ServerListBuilder.TableName = strServerListTableName;
ServerListBuilder.DataColumns.Add("ServerID");
ServerListBuilder.DataColumns.Add("ServerIP");
ServerListBuilder.DataColumns.Add("ServerBuyTime");
// tombstone表及其相關列
ServerListBuilder.TombstoneTableName = strServerListTableName + "_tombstone";
ServerListBuilder.TombstoneDataColumns.Add("ServerID");
ServerListBuilder.TombstoneDataColumns.Add("ServerIP");
ServerListBuilder.TombstoneDataColumns.Add("ServerBuyTime");
// 相關的跟蹤列
ServerListBuilder.CreationTrackingColumn = strCreationTrackingColumn;
ServerListBuilder.UpdateTrackingColumn = strUpdateTrackingColumn;
ServerListBuilder.DeletionTrackingColumn = strDeletionTrackingColumn;
ServerListBuilder.UpdateOriginatorIdColumn = strUpdateOriginatorIdColumn;
SyncAdapter ServerListSyncAdapter = ServerListBuilder.ToSyncAdapter();
((SqlParameter)ServerListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_last_received_anchor"]).DbType = DbType.Binary;
((SqlParameter)ServerListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_new_received_anchor"]).DbType = DbType.Binary;
serverSyncProvider.SyncAdapters.Add(ServerListSyncAdapter);
#endregion
#region UserList準備
SqlSyncAdapterBuilder UserListBuilder = new SqlSyncAdapterBuilder();
UserListBuilder.SyncDirection = SyncDirection.Bidirectional;
UserListBuilder.Connection = serverConnection;
// 主表及其相關列
UserListBuilder.TableName = strUserListTableName;
UserListBuilder.DataColumns.Add("UserID");
UserListBuilder.DataColumns.Add("UserName");
UserListBuilder.DataColumns.Add("UserPW");
// tombstone表及其相關列
UserListBuilder.TombstoneTableName = strUserListTableName + "_tombstone";
UserListBuilder.TombstoneDataColumns.Add("UserID");
UserListBuilder.TombstoneDataColumns.Add("UserName");
UserListBuilder.TombstoneDataColumns.Add("UserPW");
// 相關的跟蹤列
UserListBuilder.CreationTrackingColumn = strCreationTrackingColumn;
UserListBuilder.UpdateTrackingColumn = strUpdateTrackingColumn;
UserListBuilder.DeletionTrackingColumn = strDeletionTrackingColumn;
UserListBuilder.UpdateOriginatorIdColumn = strUpdateOriginatorIdColumn;
SyncAdapter UserListSyncAdapter = UserListBuilder.ToSyncAdapter();
((SqlParameter)UserListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_last_received_anchor"]).DbType = DbType.Binary;
((SqlParameter)UserListSyncAdapter.SelectIncrementalInsertsCommand.Parameters["@sync_new_received_anchor"]).DbType = DbType.Binary;
serverSyncProvider.SyncAdapters.Add(UserListSyncAdapter);
#endregion
#endregion
#region 數據同步
SqlCommand anchorCmd = new SqlCommand();
anchorCmd.CommandType = CommandType.Text;
anchorCmd.CommandText = "Select @" + SyncSession.SyncNewReceivedAnchor + "= @@DBTS";// " = @@DBTS";
anchorCmd.Parameters.Add("@" + SyncSession.SyncNewReceivedAnchor, SqlDbType.Timestamp).Direction = ParameterDirection.Output;
serverSyncProvider.SelectNewAnchorCommand = anchorCmd;
SqlCommand clientIdCmd = new SqlCommand();
clientIdCmd.CommandType = CommandType.Text;
clientIdCmd.CommandText = "SELECT @" + SyncSession.SyncOriginatorId + " = 1";
clientIdCmd.Parameters.Add("@" + SyncSession.SyncOriginatorId, SqlDbType.Int).Direction = ParameterDirection.Output;
serverSyncProvider.SelectClientIdCommand = clientIdCmd;
syncAgent.Synchronize();
#endregion
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
#endregion
#region 增量操作
#region 產生各種隨機數據
/// <summary>
/// 產生隨機IP
/// </summary>
/// <returns></returns>
private string ReturnRandomIP(Random rand)
{
string sRe = "192.168." + rand.Next(0, 256).ToString() + "." + rand.Next(0, 256).ToString();
return sRe;
}
/// <summary>
/// 產生隨機ID
/// </summary>
/// <param name="rand"></param>
/// <returns></returns>
private string ReturnRandomID(Random rand)
{
return rand.Next((int)(DateTime.Now.ToFileTime() % 10000)).ToString();
}
/// <summary>
/// 產生隨機字符
/// </summary>
/// <param name="rand"></param>
/// <returns></returns>
private char ReturnRandomChar(Random rand)
{
int ret = rand.Next(122);
while (ret < 48 || (ret > 57 && ret < 65) || (ret > 90 && ret < 97))
{
ret = rand.Next(122);
}
return (char)ret;
}
/// <summary>
/// 產生隨機字符串
/// </summary>
/// <param name="rand"></param>
/// <returns></returns>
private string ReturnRandomString(Random rand)
{
int length = 10;
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++)
{
sb.Append(ReturnRandomChar(rand));
}
return sb.ToString();
}
#endregion
/// <summary>
/// 返回一個隨機操作的ID
/// </summary>
private string ReturnRandomUpdateOrDeleteID(Random rand)
{
DataTable dtRef = new DataTable();
if (strOperationTable == strServerListTableName)
{
if (blnOperationClient)
{
dtRef = (DataTable)dgSLClient.DataSource;
}
else
{
dtRef = (DataTable)dgSLServer.DataSource;
}
}
else
{
if (blnOperationClient)
{
dtRef = (DataTable)dgULClient.DataSource;
}
else
{
dtRef = (DataTable)dgULServer.DataSource;
}
}
int iTotalRows = dtRef.Rows.Count;
return dtRef.Rows[rand.Next(0, iTotalRows)][0].ToString();
}
/// <summary>
/// 具體執行增刪改的操作
/// </summary>
/// <param name="CommandString"></param>
private void ExecuteOperation(string CommandString)
{
if (blnOperationClient)
{
#region 客戶端操作
if (CheckClientDb())
{
SqlCeConnection conn = new SqlCeConnection(strClientConnectString);
SqlCeCommand cmd = new SqlCeCommand();
cmd.Connection = conn;
cmd.CommandText = CommandString.ToString();
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (System.Exception exp)
{
MessageBox.Show(exp.Message);
}
finally
{
conn.Close();
}
}
else
{
MessageBox.Show(strNoClientDBWarnString);
}
#endregion
}
else
{
#region 服務器端操作
SqlConnection conn = new SqlConnection(strServerConnectString);
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandText = CommandString;
try
{
conn.Open();
cmd.ExecuteNonQuery();
}
catch (System.Exception exp)
{
MessageBox.Show(exp.Message);
}
finally
{
conn.Close();
}
#endregion
}
RefreshData(strOperationTable);
}
/// <summary>
/// 更新
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btUpdate_Click(object sender, EventArgs e)
{
btUpdate.Enabled = false;
#region 初始化
GetEnvironmentValue();
Random rand = new Random();
StringBuilder sSql = new StringBuilder();
if (strOperationTable == strServerListTableName)
{
//update ServerList set ServerIP='123.12',ServerBuyTime=GetDate() where ServerID=1
sSql.Append(@" update ServerList ");
sSql.Append(@" set ServerIP='"+ReturnRandomIP(rand)+"', "); //ServerIP
sSql.Append(@" ServerBuyTime=GetDate() "); //ServerBuyTime
sSql.Append(@" where ServerID="+ReturnRandomUpdateOrDeleteID(rand)+" "); //UpdateID
}
else
{
sSql.Append(@" update UserList ");
sSql.Append(@" set UserName = '"+ReturnRandomString(rand)+"', "); //UserName
sSql.Append(@" UserPW = '"+ReturnRandomString(rand)+"' "); //UserPW
sSql.Append(@" where UserID="+ReturnRandomUpdateOrDeleteID(rand)+" "); //UpdateID
}
#endregion
ExecuteOperation(sSql.ToString());
btUpdate.Enabled = true;
}
/// <summary>
/// 添加
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btAdd_Click(object sender, EventArgs e)
{
btAdd.Enabled = false;
#region 初始化
GetEnvironmentValue();
Random rand = new Random();
StringBuilder sSql = new StringBuilder();
if (strOperationTable == strServerListTableName)
{
sSql.Append(@" insert into ServerList (ServerID, ServerIP, ServerBuyTime) ");
sSql.Append(@" values( ");
sSql.Append(@" " + ReturnRandomID(rand) + ", "); //ServerID
sSql.Append(@" '" + ReturnRandomIP(rand) + "', "); //ServerIP
sSql.Append(@" GetDate() "); //ServerBuyTime
sSql.Append(@" ) ");
}
else
{
sSql.Append(@" insert into UserList (UserID, UserName, UserPW) ");
sSql.Append(@" values( ");
sSql.Append(@" " + ReturnRandomID(rand) + ", "); //UserID
sSql.Append(@" '" + ReturnRandomString(rand) + "', "); //UserName
sSql.Append(@" '" + ReturnRandomString(rand) + "' "); //UserPW
sSql.Append(@" ) ");
}
#endregion
ExecuteOperation(sSql.ToString());
btAdd.Enabled = true;
}
/// <summary>
/// 刪除
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btDelete_Click(object sender, EventArgs e)
{
btDelete.Enabled = false;
#region 初始化
GetEnvironmentValue();
Random rand = new Random();
StringBuilder sSql = new StringBuilder();
if (strOperationTable == strServerListTableName)
{
sSql.Append(@"delete from ServerList where ServerID="+ReturnRandomUpdateOrDeleteID(rand));
}
else
{
sSql.Append(@"delete from UserList where UserID="+ReturnRandomUpdateOrDeleteID(rand));
}
#endregion
ExecuteOperation(sSql.ToString());
btDelete.Enabled = true;
}
#endregion
}
}
Demo與源碼下載: