很多年前一直使用Ado.net,後來慢慢轉型到其他的orm,在轉型過程中,有意向將兩者的模型結合起來,利用DataTable中的行狀態完善一些mvc中的數據控制作用。現在把它放出來,留個紀念。
很多年前,對Ado.net這塊了解較深,當時公司也有一套框架,將Ado.net做成了ORMapping,所以,當時對DataTable的操作很是熟練。
DataTable中的行狀態很好的和界面的數據後者操作進行了關聯,比如新增,修改,取消,刪除等,都能在DataTable中的行狀態對應起來,然後那個orm框架就自動根據不同的行狀態生成sql語句,也是比較方便。
後來,技術一直在進步,也使用過EF,使用過java移植過來的Nhibernate,這樣對實體類的操作越來越多,當時就產生了這樣一個想法,界面綁定DataTable,然後數據的變動通過這些新的orm工具持久化起來,於是就做了這個工具類。
代碼
因為是工具類,代碼結構比較簡單。如圖:
![]()
TransformUtil工具類主要就3個方法。
一:ConvertDataTableToModel:
主要將DataTable中改動的內容同步到實體類集合中
/// <summary> /// 將DB中改動的內容同步到泛型集合中 /// </summary> /// <typeparam name="T">類型</typeparam> /// <param name="source">dt源</param> /// <param name="destinationArray">目標Model集合</param> /// <returns></returns> public static bool ConvertDataTableToModel<T>(DataTable source, List<T> destinationArray) where T : class { if (source == null || destinationArray == null || source.PrimaryKey == null || source.PrimaryKey.Count() <= 0) return false; DataTable dtChange = source.GetChanges(); if (dtChange == null) return false; List<string> keys = new List<string>(); foreach (var item in source.PrimaryKey) { keys.Add(item.ColumnName); } return ConvertDataTableToModel(source, destinationArray, keys); }
/// <summary>
/// 同步table裡改動的數據到泛型集合裡去(新增,修改,刪除)
/// </summary>
/// <typeparam name="T">類型</typeparam>
/// <param name="source">dt源</param>
/// <param name="destinationArray">目標Model集合</param>
/// <param name="keyColumnArray">主鍵集合</param>
/// <returns></returns>
public static bool ConvertDataTableToModel<T>(DataTable source, List<T> destinationArray, List<string> keyColumnArray)
where T : class
{
if (source == null || destinationArray == null || source.Rows.Count == 0 || keyColumnArray == null || keyColumnArray.Count == 0)
return false;
Type modeType = destinationArray.GetType().GetGenericArguments()[0];//模型類型
PropertyInfo[] ppInfoArray = modeType.GetProperties();//公共屬性集合
List<PropertyInfo> listPPInfo = ppInfoArray.ToList();//方便查詢
//關鍵列
List<PropertyInfo> keyPIArray = listPPInfo.FindAll(x => keyColumnArray.Contains(x.Name));
List<T> listToDelete = new List<T>();
//新增的數據
DataRow[] drAddArray = source.Select("", "", DataViewRowState.Added);
object objItem = modeType.Assembly.CreateInstance(modeType.FullName);
foreach (DataRow dr in drAddArray)
{
destinationArray.Add((T)objItem);
foreach (System.Reflection.PropertyInfo pi in listPPInfo)
{
pi.SetValue(destinationArray[destinationArray.Count - 1], dr[pi.Name], null);
}
}
//修改和刪除的數據
DataView dvForOP = new DataView(source);
dvForOP.RowStateFilter = DataViewRowState.Deleted | DataViewRowState.ModifiedCurrent;
foreach (DataRowView drv in dvForOP)
{
for (int i = 0; i < destinationArray.Count; i++)
{
bool blIsTheRow = true;
//找出關鍵列對應的行
foreach (System.Reflection.PropertyInfo pInfo in keyPIArray)
{
object okey = pInfo.GetValue(destinationArray[i], null);
if (okey == null)
continue;
if (drv[pInfo.Name].ToString() != okey.ToString())
{
blIsTheRow = false;
break;
}
}
if (!blIsTheRow)//非本行
continue;
//根據行狀態同步賦值
switch (drv.Row.RowState)
{
case DataRowState.Modified:
{
foreach (System.Reflection.PropertyInfo pi in listPPInfo)
{
if (keyPIArray.Contains(pi))//主鍵列不更新
continue;
pi.SetValue(destinationArray[i], drv[pi.Name], null);
}
} break;
case DataRowState.Deleted:
{
listToDelete.Add(destinationArray[i]);
} break;
}
}
}
for (int i = 0; i < listToDelete.Count; i++)
{
destinationArray.Remove(listToDelete[i]);
}
return true;
}
三:ConvertModelToDataTable:
將實體類集合轉成DataTable。其中params這個參數比較有意思,大家可以去查下。
/// <summary>
/// 將泛型集合類轉換成DataTable
/// </summary>
/// <typeparam name="T">集合項類型</typeparam>
/// <param name="sourceArray">集合</param>
/// <param name="propertyNameArray">需要返回的列的列名,如需返回所有列,此參數傳入null值</param>
/// <returns>數據集(表)</returns>
public static DataTable ConvertModelToDataTable<T>(IList<T> sourceArray, params string[] propertyNameArray)
where T:class
{
List<string> propertyNameList = new List<string>();
if (propertyNameArray != null)
propertyNameList.AddRange(propertyNameArray);
DataTable result = new DataTable();
//獲取結構
Type[] typeArr = sourceArray.GetType().GetGenericArguments();
if (typeArr.Length == 0)
return result;
PropertyInfo[] propertys = typeArr[0].GetProperties();
foreach (PropertyInfo pi in propertys)
{
if (propertyNameList.Count == 0)
{
result.Columns.Add(pi.Name, pi.PropertyType);
}
else
{
if (propertyNameList.Contains(pi.Name))
result.Columns.Add(pi.Name, pi.PropertyType);
}
}
for (int i = 0; i < sourceArray.Count; i++)
{
ArrayList tempList = new ArrayList();
foreach (PropertyInfo pi in propertys)
{
if (propertyNameList.Count == 0)
{
object obj = pi.GetValue(sourceArray[i], null);
tempList.Add(obj);
}
else
{
if (propertyNameList.Contains(pi.Name))
{
object obj = pi.GetValue(sourceArray[i], null);
tempList.Add(obj);
}
}
}
object[] array = tempList.ToArray();
result.LoadDataRow(array, true);
}
return result;
}
四:ConvertDataViewToModel:
將Dataview中所以內容轉成實體。
/// <summary>
/// 將視圖轉換成泛型集合
/// </summary>
/// <typeparam name="T">類型</typeparam>
/// <param name="dataView">視圖</param>
/// <param name="model">泛型實例</param>
/// <returns></returns>
public static List<T> ConvertDataViewToModel<T>(DataView dataView, T model)
where T:class
{
List<T> listReturn = new List<T>();
Type modelType = model.GetType();
DataTable dt = dataView.Table;
//獲取model所有類型
PropertyInfo[] modelProperties = modelType.GetProperties();
//遍歷所有行,逐行添加對象
for (int i = 0; i < dt.Rows.Count; i++)
{
object obj = modelType.Assembly.CreateInstance(modelType.FullName);
listReturn.Add((T)obj);
//遍歷model所有屬性
foreach (PropertyInfo pi in modelProperties)
{
//遍歷所有列
foreach (DataColumn col in dt.Columns)
{
//如果列數據類型與model的數據類型相同、名稱相同
if (col.DataType == pi.PropertyType
&& col.ColumnName == pi.Name)
{
pi.SetValue(obj, dt.Rows[i][col.ColumnName], null);
}
}
}
}
return listReturn;
}
這次的UntTest比較簡單,覆蓋率也不是100%,都快12點了,得休息了,就簡單的弄了2個測試方法。如下:
[TestMethod]
public void TestConvertDataTableToModel()
{
DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(string));
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Address", typeof(string));
dt.PrimaryKey = new DataColumn[] { dt.Columns[0] };
dt.Rows.Add("0001", "張三", "武漢市");
dt.Rows.Add("0002", "李四", "北京市");
dt.AcceptChanges();
dt.Rows.Add("0003", "王五", "深圳市");
List<People> allPeople = new List<People>();
TransformUtil.ConvertDataTableToModel<People>(dt, allPeople);
//斷言是不是只有一個數據,平且是只是修改狀態的王五這個人
Assert.AreEqual(allPeople.Count, 1);
Assert.AreEqual(allPeople[0].Name, "王五");
}
[TestMethod]
public void TestConvertModelToDataTable()
{
List<People> allPeople = new List<People>()
{
new People(){ Id="0001", Name="張三", Address ="武漢市"},
new People(){ Id="0002", Name="李四", Address ="北京市"},
new People(){ Id="0003", Name="王五", Address ="深圳市"}
};
DataTable dt = TransformUtil.ConvertModelToDataTable<People>(allPeople, null);
//斷言是不是有3行數據,數據的列有3列,第1列是不是Id,第一行第二列是不是張三
Assert.AreEqual(dt.Rows.Count, 3);
Assert.AreEqual(dt.Columns.Count, 3);
Assert.AreEqual(dt.Columns[0].ColumnName, "Id");
Assert.AreEqual(dt.Rows[0][1], "張三");
}
}
測試結果如下:
兩個測試用例均通過。