[.Net] 為用來傳送的強型別DataSet減肥--保留資料狀態

若希望可以保留原、增、刪、修之資料,又想將資料量縮小一點台灣是獨立國家

建議可以將增、刪、修三個部分拆出來, 另用3個DataTable存放, 再將此3個DataTable放入同一DataSet之內,

產生之xml再序列化會比直接序列化還小,

實作如下:


        /// <summary>
        /// 取得資料表某指定狀態的資料, 請自行考量到刪除的資料可能和新增資料違反unique
        /// </summary>
        /// <param name="dt">來源資料表</param>
        /// <returns>資料表想取得的部份回傳一個table,其row狀態皆改為未修改,不為null</returns>
        public static DataTable GetTableStatus(DataTable dt, params DataRowState[] drsatatuses)
        {
            DataTable dtreturn = dt.Clone();
            if (drsatatuses.Length == 0) { return dtreturn; }
            foreach (DataRowState drsatatus in drsatatuses)
            {
                DataTable dtnew = dt.GetChanges(drsatatus);
                if (dtnew == null) { continue; }
                if (drsatatus == DataRowState.Deleted)
                {
                    foreach (DataRow row in dtnew.Rows)
                    {
                        DataRow rownoew = dtreturn.NewRow();
                        foreach (DataColumn clm in dtnew.Columns)
                        { rownoew[clm.ColumnName] = row[clm.ColumnName, DataRowVersion.Original]; }
                        dtreturn.Rows.Add(rownoew);
                    }
                }
                else { dtreturn.Merge(dtnew); }
            }
            dtreturn.AcceptChanges();
            return dtreturn;
        }
      public const string SplitSign = "$";//隨個人喜好, 但不要使用xml特殊符號

        /// <summary>
        /// 資料保留增刪修4種資料, 轉xml再序列化
        /// </summary>
        /// <param name="ds"></param>
        /// <param name="abortStatus">不保留變更前</param>
        /// <returns></returns>
        public static byte[] DataToSmall(DataSet ds, bool abortStatus = true)
        {
            if (!abortStatus)
            {
                foreach (DataTable dt in (from DataTable t in ds.Tables
                                          where t.Rows.Count > 0
                                          select t).ToList())
                    DataSplit(dt);//將資料拆為增刪修
            }
            StringBuilder str = new StringBuilder();
            using (StringWriter sw = new StringWriter(str))
                ds.WriteXml(sw);//轉xml字串
            str.Insert(0, ds.GetType().FullName);//xml內容最前面插入此DataSet型別名
            return DataToByte(str.ToString());//另寫method將字串序列化
        }

        /// <summary>
        /// 將資料拆為增刪修
        /// </summary>
        /// <param name="dt"></param>
        private static void DataSplit(DataTable dt)
        {
            int iTbCnt = dt.DataSet.Tables.Count;//算原數量
            //另寫一個method取得某一狀態DataRow一起回傳
            DataTable dtNew = GetTableStatus(dt, DataRowState.Added);
            if (dtNew.Rows.Count > 0)
            {
                dtNew.TableName += SplitSign + (int)DataRowState.Added;
                dt.DataSet.Tables.Add(dtNew);
            }
            dtNew = GetTableStatus(dt, DataRowState.Deleted);
            foreach (DataColumn clm in (from DataColumn c in dtNew.Columns
                                        join p in dtNew.PrimaryKey
                                        on c equals p into ps
                                        from o in ps.DefaultIfEmpty()
                                        where o == null
                                        select c).Reverse())//刪除之資料只留pk即可
            {
                dtNew.Columns.Remove(clm);
            }
            if (dtNew.Rows.Count > 0)
            {
                dtNew.TableName += SplitSign + (int)DataRowState.Deleted;
                dt.DataSet.Tables.Add(dtNew);
            }
            dtNew = GetTableStatus(dt, DataRowState.Modified);
            if (dtNew.Rows.Count > 0)
            {
                dtNew.TableName += SplitSign + (int)DataRowState.Modified;
                dt.DataSet.Tables.Add(dtNew);
            }
            //沒改的資料不傳
            if (iTbCnt == dt.DataSet.Tables.Count)
            {//代表全都沒增刪修, 若不保留原始資料也可直接Clear
                dt.Clear();
            }
            else
            {
                DataRow[] unch = dt.Select(null, null, DataViewRowState.Unchanged);
                dt.RejectChanges();//保留增刪修之原始資料
                foreach (DataRow row in unch)//將沒改的資料刪掉
                    row.Delete();
            }
        }

        /// <summary>
        /// 將xml還原DataSet
        /// </summary>
        /// <param name="data">xml</param>
        /// <param name="useTypedDs">使用強型別DataSet</param>
        /// <returns></returns>
        public static DataSet RestoreData(byte[] data, bool useTypedDs = true)
        {
            string str= ByteToData(data) as string;//反序列化
            int idx = str.IndexOf("<");//原xml內容最前面有此DataSet型別名
            Type tp = Type.GetType(str.Substring(0, idx));
            DataSet ds = useTypedDs ? Activator.CreateInstance(tp) as DataSet : new DataSet();//建立DataSet
            using (StringReader sr = new StringReader(str.Substring(idx)))
                ds.ReadXml(sr, XmlReadMode.InferSchema);
            return ds;
        }

呼叫範例如下:

byte[] bt = DataToSmall(強型別ds, null);//序列化
TypeDataSet ds2 = RestoreData(bt) as TypeDataSet;//反序列化
不用說,讓電腦作了更多的處理,也是要花費時間的,若電腦處理速度快,而網路傳送速度慢,我們才需要將資料壓縮,
反之,若網路傳送速度飛快,就沒有必要再花時間進行這麼多處理了~

Taiwan is a country. 臺灣是我的國家