Skip to content

Instantly share code, notes, and snippets.

@gyuwon
Last active August 29, 2015 14:10
Show Gist options
  • Save gyuwon/cd41863e97d817d4436e to your computer and use it in GitHub Desktop.
Save gyuwon/cd41863e97d817d4436e to your computer and use it in GitHub Desktop.
DataTable to List<T>
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;
namespace DataTableMapper
{
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public double Value { get; set; }
public override string ToString()
{
return string.Format(@"{{ ""Id"": {0}, ""Name"": ""{1}"", ""Value"": {2} }}", Id, Name, Value);
}
}
public static class Program
{
/// <summary>
/// <see cref="DataTable"/> 개체λ₯Ό <see cref="List&lt;T&gt;"/>둜 λ³€ν™˜ν•©λ‹ˆλ‹€.
/// </summary>
/// <typeparam name="T"><see cref="DataRow"/>λ₯Ό νˆ¬μ˜ν•  λŒ€μƒ ν˜•μ‹μž…λ‹ˆλ‹€.</typeparam>
/// <param name="table">μ†ŒμŠ€ ν…Œμ΄λΈ”μž…λ‹ˆλ‹€.</param>
/// <returns><paramref name="table"/>을 λ³€ν™˜ν•œ <see cref="List&lt;T&gt;"/>μž…λ‹ˆλ‹€.</returns>
public static List<T> ToList<T>(DataTable table)
where T : new()
{
return ToList(table, () => new T(), (c, p) => c.ColumnName == p.Name);
}
/// <summary>
/// <see cref="DataTable"/> 개체λ₯Ό <see cref="List&lt;T&gt;"/>둜 λ³€ν™˜ν•©λ‹ˆλ‹€.
/// </summary>
/// <typeparam name="T"><see cref="DataRow"/>λ₯Ό νˆ¬μ˜ν•  λŒ€μƒ ν˜•μ‹μž…λ‹ˆλ‹€.</typeparam>
/// <param name="table">μ†ŒμŠ€ ν…Œμ΄λΈ”μž…λ‹ˆλ‹€.</param>
/// <param name="factory"><typeparamref name="T"/> 개체λ₯Ό μƒμ„±ν•˜λŠ” λ©”μ„œλ“œμž…λ‹ˆλ‹€.</param>
/// <param name="match"><see cref="DataColumn"/>κ³Ό μ†μ„±μ˜ 일치 μ—¬λΆ€λ₯Ό λ°˜ν™˜ν•˜λŠ” λ©”μ„œλ“œμž…λ‹ˆλ‹€.</param>
/// <returns><paramref name="table"/>을 λ³€ν™˜ν•œ <see cref="List&lt;T&gt;"/>μž…λ‹ˆλ‹€.</returns>
public static List<T> ToList<T>(DataTable table,
Func<T> factory,
Func<DataColumn, PropertyInfo, bool> match)
{
// μ‘°νšŒμ™€ 섀정이 κ°€λŠ₯ν•œ 곡용 μΈμŠ€ν„΄μŠ€ 속성 λͺ©λ‘μ„ κ°€μ Έμ˜΅λ‹ˆλ‹€.
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty | BindingFlags.SetProperty);
// 속성 값을 μ„€μ •ν•˜λŠ” λ©”μ„œλ“œ λŒ€λ¦¬μžλ₯Ό λΉŒλ“œν•˜λŠ” GetSetter<,>(MethodInfo) λ©”μ„œλ“œμ˜ μ œλ„€λ¦­ μ •μ˜λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
// λŒ€λ¦¬μžλ₯Ό λΉŒλ“œν•˜λŠ” λͺ©μ μ€ μ„±λŠ₯적으둜 λΆˆλ¦¬ν•œ λ¦¬ν”Œλ ‰μ…˜ μž‘μ—…μ„ μ΅œμ†Œν•˜ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.
var getSetterTemplate = typeof(Program).GetMethod("GetSetter", BindingFlags.NonPublic | BindingFlags.Static);
// 데이터 μ—΄ 값을 속성에 νˆ¬μ˜ν•˜λŠ” ν”„λ‘œμ„ΈμŠ€ λͺ©λ‘μ„ μΊμ‹±ν•©λ‹ˆλ‹€.
var setters = table.Columns
.Cast<DataColumn>()
.Select((column, index) =>
{
// columnκ³Ό μΌμΉ˜ν•˜λŠ” 속성을 μ°ΎμŠ΅λ‹ˆλ‹€. λ‘˜ μ΄μƒμ˜ 속성이 μΌμΉ˜ν•˜λ©΄ μ˜ˆμ™Έκ°€ λ°œμƒν•©λ‹ˆλ‹€.
var property = properties.SingleOrDefault(p => match(column, p));
if (property != null &&
property.PropertyType.IsAssignableFrom(column.DataType))
{
// columnκ³Ό μΌμΉ˜ν•˜λŠ” 속성이 μ‘΄μž¬ν•˜κ³  데이터 값을 속성에 λŒ€μž…ν•  수 μžˆλŠ” κ²½μš°μž…λ‹ˆλ‹€.
// 속성 ν˜•μ‹μ— λŒ€ν•œ GetSetter<,>(MethodInfo) μ œλ„€λ¦­ λ©”μ„œλ“œλ₯Ό λ§Œλ“€μ–΄ ν˜ΈμΆœν•©λ‹ˆλ‹€.
var getSetter = getSetterTemplate.MakeGenericMethod(typeof(T), property.PropertyType);
var setter = (Action<T, object>)getSetter.Invoke(null, new object[] { property.GetSetMethod() });
// μ—΄ μΈλ±μŠ€μ™€ 속성 μ„€μ • λ©”μ„œλ“œλ₯Ό μΊ‘μŠν™”ν•΄ λ°˜ν™˜ν•©λ‹ˆλ‹€.
return new { ColumnIndex = index, Setter = setter };
}
return null;
})
.Where(setter => setter != null)
.ToList();
// DataRowλ₯Ό T ν˜•μ‹μœΌλ‘œ νˆ¬μ˜ν•˜λŠ” λ©”μ„œλ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
Func<DataRow, T> selector = row =>
{
T instance = factory();
foreach (var setter in setters)
{
var value = row[setter.ColumnIndex];
setter.Setter(instance, value);
}
return instance;
};
// table을 List<>둜 λ³€ν™˜ν•΄ λ°˜ν™˜ν•©λ‹ˆλ‹€.
return table.AsEnumerable().Select(selector).ToList();
}
private static Action<TComponent, object> GetSetter<TComponent, TProperty>(MethodInfo setMethod)
{
// 속성 μ„€μ • λ©”μ„œλ“œλ₯Ό 정적(static) ν˜•νƒœλ‘œ λΉŒλ“œν•©λ‹ˆλ‹€.
// 정적 ν˜•νƒœλ‘œ λΉŒλ“œν•˜λ©΄ 첫번째 λ§€κ°œλ³€μˆ˜λ‘œ μΈμŠ€ν„΄μŠ€λ₯Ό μ „λ‹¬ν•˜κ²Œ λ©λ‹ˆλ‹€.
var setter = (Action<TComponent, TProperty>)setMethod.CreateDelegate(typeof(Action<TComponent, TProperty>));
// μΈμŠ€ν„΄μŠ€μ— 속성 값을 μ„€μ •ν•˜λŠ” λ©”μ„œλ“œλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
return (component, propertyValue) => setter(component, (TProperty)propertyValue);
}
public static void Main(string[] args)
{
var table = new DataTable
{
Columns =
{
new DataColumn("Id", typeof(int)),
new DataColumn("Name", typeof(string)),
new DataColumn("Value", typeof(double)),
new DataColumn("NotMapped", typeof(string))
}
};
var row1 = table.NewRow();
row1["Id"] = 1;
row1["Name"] = "Foo";
row1["Value"] = 256;
row1["NotMapped"] = "Hello";
table.Rows.Add(row1);
var row2 = table.NewRow();
row2["Id"] = 2;
row2["Name"] = "Bar";
row2["Value"] = 65536;
row2["NotMapped"] = "World";
table.Rows.Add(row2);
foreach (var item in ToList<Item>(table))
{
Console.WriteLine(item);
}
}
}
}
@gyuwon
Copy link
Author

gyuwon commented Nov 24, 2014

Output:

{ "Id": 1, "Name": "Foo", "Value": 256 }
{ "Id": 2, "Name": "Bar", "Value": 65536 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment