[.NET]將DataTable轉成List物件

雖然有很多的ORM框架可以幫我們將資料轉成List<物件>,但如果手動要將DataTable轉成List<物件>,有那些方式呢?

今天看同事分享時,看到他們將DataTable轉成物件時,使用for...each的方式。

雖然有很多的ORM框架可以幫我們做到同樣的效果,但如果手動要做的話,有那些方式呢?

//要轉換的物件
public class Document
{
	public string CompId { get; set; }
	public string HandleUnit { get; set; }
	public string No { get; set; }
}

 

方法1:使用for...each (for…next也可以)

//將DataTable轉成List<物件>
DataTable dt = new DataTable();
dt.Columns.Add("CompId", typeof(string));
dt.Columns.Add("HandleUnit", typeof(string));
dt.Columns.Add("No", typeof(string));
dt.Rows.Add(new object[] {"655", "EBS", "001" });
dt.Rows.Add(new object[] { "655", "ODM", "002" });
dt.Rows.Add(new object[] { "655", "OCS", "003" });

//Way 1, for next
List<Document> Way1 = new List<Document>();
foreach (DataRow dr in dt.Rows)
{
	Document doc = new Document();
	doc.CompId = dr.Field<string>("CompId");
	doc.HandleUnit = dr.Field<string>("HandleUnit");
	doc.No = dr.Field<string>("No");
	Way1.Add(doc);
}

 

方式2:建立Extension Methods透過Reflection來簡化(參考:fetch datarow to c# object)

public static class DataTableExtensions
{
	public static IList<T> ToList<T>(this DataTable table) where T : new()
	{
		IList<PropertyInfo> properties = typeof(T).GetProperties().ToList();
		IList<T> result = new List<T>();

		foreach (var row in table.Rows)
		{
			var item = CreateItemFromRow<T>((DataRow)row, properties);
			result.Add(item);
		}

		return result;
	}

	public static IList<T> ToList<T>(this DataTable table, Dictionary<string, string> mappings) where T : new()
	{
		IList<PropertyInfo> properties = typeof(T).GetProperties().ToList();
		IList<T> result = new List<T>();

		foreach (var row in table.Rows)
		{
			var item = CreateItemFromRow<T>((DataRow)row, properties, mappings);
			result.Add(item);
		}

		return result;
	}

	private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
	{
		T item = new T();
		foreach (var property in properties)
		{
			property.SetValue(item, row[property.Name], null);
		}
		return item;
	}

	private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties, Dictionary<string, string> mappings) where T : new()
	{
		T item = new T();
		foreach (var property in properties)
		{
			if (mappings.ContainsKey(property.Name))
				property.SetValue(item, row[mappings[property.Name]], null);
		}
		return item;
	}
}

 

方式2.1:如果欄位名稱跟物件屬性名稱相同,則直接對應。

DataTable dt2 = new DataTable();
dt2.Columns.Add("CompId", typeof(string));
dt2.Columns.Add("HandleUnit", typeof(string));
dt2.Columns.Add("No", typeof(string));
dt2.Rows.Add(new object[] { "655", "EBS", "001" });
dt2.Rows.Add(new object[] { "655", "ODM", "002" });
dt2.Rows.Add(new object[] { "655", "OCS", "003" });
//Way 2, 如果欄位名稱跟屬性一樣,就直接Assign
var Wary2 = dt2.ToList<Document>();

 

方式2.2:如果欄位名稱跟物件屬性名稱不同,則建立Dictionary型態的Mapping物件,如下,

DataTable dt3 = new DataTable();
dt3.Columns.Add("CompId", typeof(string));
dt3.Columns.Add("HandleUnit", typeof(string));
dt3.Columns.Add("No", typeof(string));
dt3.Rows.Add(new object[] { "655", "EBS", "001" });
dt3.Rows.Add(new object[] { "655", "ODM", "002" });
dt3.Rows.Add(new object[] { "655", "OCS", "003" });
//Way 3, 如果欄位跟屬性不同,就建一個Mapping表

var mappings = new Dictionary<string, string>();
mappings.Add("CompId", "CompId");
mappings.Add("HandleUnit", "HandleUnit");
mappings.Add("No", "No");
var Way3 = dt3.ToList<Document>(mappings);

 

相信一定有一堆人會問說,為何要這樣做呢? 為何不使用ORM呢?  為什麼? 為什麼? …..等很多的疑問

同事回說,因為只是針對舊有的系統新增一個小的模組,而目前系統的Table有300多個,時間有限 ... So.... 就先這樣搞…

 

參考資料

fetch datarow to c# object

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^