Winform開發之離線式WCF開發框架的實現介紹


在上篇隨筆《Winform開發框架之框架演化》中介紹了幾種Winform開發框架,其中有對於離線式WCF開發框架的介紹,離線式的WCF開發框架 ,就是結合了傳統Winform開發框架的數據訪問方式,又利用了WCF分布式數據獲取的特點,使得數據可以離線使用,在一種業務要求集中化,又要求不影響正常業務操作的應用系統場景下比較適合。本文主要介紹如何利用我的Winform開發框架的整體思路,實現WCF開發框架的離線式的數據上傳、更新的同步操作。

其實目前企業集中化管理,這種模式要求很多,如一些加盟店的情況,需要獨立運行,有可以對一些總店關鍵數據進行提交或者下載,如客戶信息等。這種情況下,就要求我們開發者提供適合應用場景的開發框架進行支持。離線式的WCF開發框架,一個特點就是基本上顯示,以及保存等操作數據庫的數據,都是本地的數據庫,不是遠端的服務器數據庫,這樣,就需要記錄所有發生變更的數據庫操作,包括寫入,刪除、修改等,以便在網絡暢通的情況下,可以上傳數據到服務器上面。

下面我們來分析下這種離線式的WCF開發框架,需要做哪些准備工作,來實現框架的支撐。

1、數據庫表記錄ID定義唯一性。

這個是常見分布式系統的要求了,在一些普通的Winform程序的數據庫中也比較常見,之所以把它作為第一條,雖然簡單,但是很必要,因為需要避免分布式的客戶端和服務端的數據沖突問題,特別在多個客戶端的情況下,對數據的唯一性要有好的控制性。

所以這也要求基礎的框架基類,能夠提供對整形、字符型的主鍵ID的操作兼容性,這在我的Winform開發框架中,支持是比較好的。

 

2、多數據庫支持

在分布式的環境下,和服務端的環境不同,部署程序要求越簡單越好,太復雜的話,增加客戶端的使用的難度,會極大提高維護的成本,因此,一般客戶端會選用適應性比較好,又免安裝的數據庫,如Sqlite就是一個很好的單機版數據庫,還有Access也是很不錯的,當然還有其他的一些數據庫,不過我覺得Sqlite和Access是比較好的備選方案。服務器端的數據庫,則看業務支持和響應程度來決定,可以從一些對性能支持比較好的數據庫中選型,如大型一點的,可選擇Oracle來做,其他的可以選擇SqlServer、MySql等數據庫。雖然這些數據庫部署比較麻煩一點,不過反正只有一台服務器需要這種安裝部署,所以工作難度及工作量不會很大。

對多數據庫的支持,也要求我們的開發框架能夠很好兼容,最好在數據庫操作層可以通過配置方式進行切換,即使數據庫變化為其他類型,也不需要改變整體的框架布局,甚至不用變化代碼即可實現自由切換,如數據庫框架可以設置如下。

 

對於上面幾種數據庫的支持,一般來說,需要增加不同數據庫類型的BaseDAL,由於每個不同數據庫都需要擁有一個BaseDAL,那么很多相同的操作代碼就會發生冗余,因為大多數數據庫的基礎操作是一樣的,只有一部分比較特別,需要進行個性化處理,因此對數據訪問層進行優化設計,得到下面的設計圖,如下所示。

經過框架抽象,這個BaseDAL類代碼很少,基本上通用的數據庫操作,已經放到了AbStractBaseDAL超級基類進行封裝,即使對於一些不同數據庫操作不同,我們也盡可能抽象放到上面基類了,BaseDAL只需要實現一些特殊的操作即可。

 

 

3、分布式客戶端數據上傳設計

由於分布式,離線式的框架設計,要求我們客戶端自行記錄數據的變化情況,包括新增數據、修改數據和刪除數據,這樣不用每次同步的時候,把所有的數據庫記錄都遍歷一次,然后和服務器記錄進行比較。這種記錄方式,可以極大提高客戶端數據上傳的性能和快捷性。因為我們對於很多表及記錄的數據庫,可能每次更新的只是一小部分,這樣設計,有利於我們更好地額處理客戶端數據上傳。

例如,下面的表,就是對於一個客戶端上傳記錄表的設計,其中Dept_ID是用來記錄不同部門的表示,基本上每個客戶端,都有自己的一個部門編號,防止數據發生沖突,也方便服務器端的數據進行歸類查詢。

下面是一些實際業務產生的數據記錄,我們記錄部門ID、表名(發生變化)、對應記錄的ID(GUID)、修改用戶、修改時間等信息。

另外,我們還可以結合系統來記錄用戶登錄信息、用戶對記錄修改的日志,以便我們對一些關鍵操作進行審計需要。數據庫設計如下所示。

 

4、數據修改記錄自動記錄

對於上面的to_upload表,我們是把客戶端修改的數據記錄信息,記錄到表里面去,但是這些肯定是后台自動記錄的,而且這個操作是放到基類比較合適,否則每次調用,不太方便,也比較冗余。

放到基類的操作,我們需要設計一下,否則所有的表都會記錄,不管需不需要,這樣不可以的。

首先我們在基類BaseDAL(對Sqlite的數據庫基類),增加一個變量來記錄是否數據庫訪問基類,需要記錄數據庫變化信息。

protected bool IsLogToUpoad = false; //表示是否記錄變化

對於具體業務對象的數據訪問,我的Winform開發框架都有提供一個對應的類來進行操作。

    /// <summary>
    /// 葯品信息
    /// </summary>
    public class DrugDetail : BaseDAL<DrugDetailInfo>, IDrugDetail
    {
        #region 對象實例及構造函數

        public static DrugDetail Instance
        {
            get
            {
                return new DrugDetail();
            }
        }
        public DrugDetail() : base("M_DrugDetail","ID")
        {
            this.IsLogToUpoad = true; this.sortField = "EditTime";
            this.isDescending = true;
        }

        #endregion
..........................................

為了要實現自動記錄數據庫變化信息,我們需要在BaseDAL里面對插入、修改、刪除的操作進行特別的處理,重載基類的操作,增加相應的處理即可,如下代碼所示。

        private void AddToUpload(string id, string targetTable, System.Data.Common.DbTransaction trans, int uploadType)
        {
            AppConfig config = new AppConfig();

            ToUploadInfo info = new ToUploadInfo();
            info.EditTime = DateTime.Now;
            info.RecordId = id;
            info.TableName = targetTable;
            info.UploadType = uploadType;
            info.Dept_ID = config.AppConfigGet("Dept_ID"); ;
            info.User_ID = config.AppConfigGet("User_ID");
            Hashtable uploadHash = GetHashByObject(info);
            base.Insert(uploadHash, "SS_ToUpload", trans);
        }

        public override bool PrivateUpdate(object id, Hashtable recordField, string targetTable, DbTransaction trans)
        {
            bool result = base.PrivateUpdate(id, recordField, targetTable, trans);
            if (result && IsLogToUpoad)
            {
                AddToUpload(recordField["ID"].ToString(), targetTable, trans, 1);
            }
            return result;
        }

        public override bool Insert(System.Collections.Hashtable recordField, string targetTable, System.Data.Common.DbTransaction trans)
        {           
            bool result = base.Insert(recordField, targetTable, trans);
            if (result && IsLogToUpoad)
            {
                AddToUpload(recordField["ID"].ToString(), targetTable, trans, 0);
            }
            return result;
        }

由於是上面的PrivateUpdate和Inser方法,是所有派生的更新、插入接口的最原始的函數,所有其他相關函數都會調用這兩個的基礎函數, 這樣就基本實現了數據庫記錄的變化記錄了。

 

5、分布式客戶端數據和服務器端的同步

為了和服務器實現同步,需要實現變化記錄的上傳和服務器修改數據的下載兩個方向的工作。

變化記錄的上傳從操作,就是遍歷to_upload里面的記錄,把它更新到服務器上即可。

        /// <summary>
        /// 把本地變化的數據記錄,同步到服務器上
        /// </summary>
        /// <returns></returns>
        public bool SyncAll()
        {
            List<ToUploadInfo> toList = BLLFactory<ToUpload>.Instance.GetAll();
            int i = 1;
            int total = toList.Count;
            bool success = false;

            foreach (ToUploadInfo toInfo in toList)
            {               
                switch(toInfo.TableName.ToLower())
                {
                    case "m_drugusedetail":
                        success = DealDrugUseDetail(toInfo);
                        if (!success) return false;
                        break;
                      .......................................
                      //其他操作,利用服務器代理對象,實現各個表的數據上傳   
                }

                #region 顯示進度等處理
                string tips = string.Format("正在同步表 {0}...", toInfo.TableName);
                int step = 0;
                if (total > 0)
                {
                    step = Convert.ToInt32((100.0 / (1.0 * total)) * i);
                }
                if (OnDataDealed != null)
                {
                    OnDataDealed(step, tips);
                }
                i++;

                if (toInfo == null || string.IsNullOrEmpty(toInfo.TableName))
                {
                    continue;
                }
                #endregion
            }

            #region 同步系統關鍵數據
            if (success)
            {
                //部門
                SynAllDept();

                //已上傳數據表同步
                DealUploaded();

                SyncBasicData();
            }

            #endregion

            return true;
        }

 為了實現數據上傳操作,我們把邏輯封裝在一個函數里面,這樣方便管理,也方便閱讀。

        private bool DealDrugUseDetail(ToUploadInfo toInfo)
        {
            bool success = false;
            DrugUseDetailInfo objInfo = BLLFactory<DrugUseDetail>.Instance.FindByID(toInfo.RecordId);
            if (objInfo != null && objInfo.Dept_ID == Portal.gc.LoginInfo.Dept_ID)
            {
                new DrugUseDetailServiceClient().Using(client =>
                {
                    success = client.InsertUpdate(objInfo, objInfo.ID);
                });

                if (success)
                {
                    RemoveToUploadInfo(toInfo);
                }
            }
            return success;
        }

數據同步的下載操作,其實也不難,就是把數據對應的記錄下載下來進行判斷。

        private void DealInHospital()
        {
            List<InHospitalInfo> list = new List<InHospitalInfo>();
            new InHospitalServiceClient().Using(client =>
            {
                list = client.Find(conditionPilotDept);
            });

            int i = 1;
            int total = list.Count;
            foreach (InHospitalInfo info in list)
            {
                BLLFactory<InHospital>.Instance.InsertUpdate(info, info.ID);
                ShowProgress(total, i++, "住院信息");
            }
        }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM