多年前寫的DataTable與實體類的轉換,已放github

sinodzh發表於2015-03-19

本文版權歸mephisto和部落格園共有,歡迎轉載,但須保留此段宣告,並給出原文連結,謝謝合作。

文章是哥(mephisto)寫的,SourceLink

閱讀目錄

本文版權歸mephisto和部落格園共有,歡迎轉載,但須保留此段宣告,並給出原文連結,謝謝合作。

文章是哥(mephisto)寫的,SourceLink

 

介紹

 很多年前一直使用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);
        }

二:ConvertDataTableToModel過載:

        /// <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;
        }

UnitTest

這次的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], "張三");
        }
    }

測試結果如下:

兩個測試用例均通過。

GitHub

https://github.com/sinodzh/TransformDTAndModel

本文版權歸mephisto和部落格園共有,歡迎轉載,但須保留此段宣告,並給出原文連結,謝謝合作。

文章是哥(mephisto)寫的,SourceLink

相關文章