直接上代码:
根据属性进行映射:DataTable转化成实体List
public static class DataTableExtension { public static List<T> ToList<T>(this DataTable dt) { if (dt == null || dt.Rows.Count == 0) { return null; } List<T> entites = new List<T>(); foreach (DataRow dr in dt.Rows) { T t = (T)Activator.CreateInstance(typeof(T)); for (int i = 0; i < dr.Table.Columns.Count; i++) { PropertyInfo propertyInfo = t.GetType().GetProperty(dr.Table.Columns[i].ColumnName); if (propertyInfo != null && dr[i] != DBNull.Value) propertyInfo.SetValue(t, dr[i], null); } entites.Add(t); } return entites; } } }
但是需求往往没有这么简单,如果属性名称和列名不一致,如列名是excel导入过来的,就很有可能是汉字。
我的解决办法就是,在实体模型中,添加Attribute,来注明每个属性和列名的对应关系,如下:
public class Bill { [Column("序号")] public int Id { get; set; } [Column("姓名")] public string PatientName { get; set; } [Column("结算日期")] public DateTime BillDate { get; set; } }
现在有两种映射方式了,为了使代码不要太难看,只能重构一下:
public static class DataTableExtension { public static List<T> ToList<T>(this DataTable dt, IRowMapper<T> rowMapper) { if (dt == null || dt.Rows.Count == 0) { return null; } List<T> entites = new List<T>(); foreach (DataRow dr in dt.Rows) { var t = rowMapper.MapRow(dr); entites.Add(t); } return entites; } }
添加了一个接口IRowMapper,表明是属性映射,还是自定义的映射。
来看下自定义映射具体的实现:
public class ColumnAttributeMapper<T> : IRowMapper<T> { private static Dictionary<string, Dictionary<string, string>> ColumnPropertyMapper= new Dictionary<string, Dictionary<string, string>>(); public ColumnAttributeMapper() { if (!ColumnPropertyMapper.ContainsKey(typeof(T).Name)) { Dictionary<string, string> dict = new Dictionary<string, string>(); var props = typeof(T).GetProperties(); foreach (var prop in props) { var attribute = prop.GetCustomAttributes(true).OfType<ColumnAttribute>().FirstOrDefault(); dict.Add(attribute.Name, prop.Name); } ColumnPropertyMapper.Add(typeof(T).Name, dict); } } public T MapRow(DataRow dr) { T t = (T)Activator.CreateInstance(typeof(T)); for (int i = 0; i < dr.Table.Columns.Count; i++) { if (ColumnPropertyMapper.ContainsKey(t.GetType().Name)) { var dict = ColumnPropertyMapper[t.GetType().Name]; var property = dict[dr.Table.Columns[i].ColumnName]; PropertyInfo propertyInfo = t.GetType().GetProperty(property); if (propertyInfo != null && dr[i] != DBNull.Value) propertyInfo.SetValue(t, dr[i], null); } } return t; } }
在构造函数中定义了一个静态的字典,用于存储实体中列名和属性名的对应关系(这里有点绕,我还没想到更好的方法!),
具体调用方法:
public static void TestPropertyMapper() { DataTable dt = new DataTable(); dt.Columns.Add("Id", typeof(int)); dt.Columns.Add("PatientName", typeof(string)); dt.Columns.Add("BillDate", typeof(DateTime)); dt.Rows.Add(1, "HKK", DateTime.Now); dt.Rows.Add(2, "WQ", DateTime.Now); dt.Rows.Add(3, "HS", DateTime.Now); List<Bill> bills = dt.ToList<Bill>(rowMapper: new PropertyColumnMapper<Bill>()); } private static void TestColumnMapper() { DataTable dt = new DataTable(); dt.Columns.Add("序号", typeof(int)); dt.Columns.Add("姓名", typeof(string)); dt.Columns.Add("结算日期", typeof(DateTime)); dt.Rows.Add(1, "HKK", DateTime.Now); dt.Rows.Add(2, "WQ", DateTime.Now); dt.Rows.Add(3, "HS", DateTime.Now); List<Bill> bills = dt.ToList<Bill>(rowMapper: new ColumnAttributeMapper<Bill>()); }
代码上传到github:https://github.com/hankuikuide/ExcelAccessor/tree/master/Han.DataAccess