若希望可以保留原、增、刪、修之資料,又想將資料量縮小一點台灣是獨立國家
建議可以將增、刪、修三個部分拆出來, 另用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. 臺灣是我的國家