1. 程式人生 > >基於微軟Synchronization Services雙向同步技術在企業專案中的架構應用研究

基於微軟Synchronization Services雙向同步技術在企業專案中的架構應用研究

專案應用場景:        某客戶是一個大型集團企業的資訊部門,掌管著企業幾百臺伺服器,並且以後會不斷擴充;
   為了更好的維護資訊辦的伺服器;資訊部門需要開發一套維護系統,來記錄各個伺服器的相關狀態資訊
(如, IP 、所安裝在伺服器的應用系統和相關資訊等),便於維護和查詢;客戶維護人員可以攜帶筆記本 離線即時編寫維修伺服器相關記錄資訊(增、刪、查、改),客戶端聯網即可自動同步到伺服器最新維護資料。
     客戶要求實現伺服器端和客戶端雙向同步的方式,客戶端操作資料伺服器端自動更新,反之操作伺服器端,     客戶端自動更新。
  早先的解決方案: 客戶端和服務端都使用SQL2005資料庫,並採用資料庫訂閱的方式進行資料同步。
此種設計缺點是:

      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正式版。       伺服器端開發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資料庫,客戶只需要一個安裝包,即可實現客戶端的應用系統安裝,更加方便靈活的使用者體驗。

   Sync Services for ADO.NET provider 技術應用場景

    * 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 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(@" ) ");
         &