我正在尝试编写一个将DataTable转换为强类型对象列表的通用方法。
到目前为止,我正在使用的代码是......
public List<T> ImportTable<T>(String fileName, String table)
{
//Establish Connection to Access Database File
var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");
var tableData = new List<T>();
foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
tableData.Add(ConvertRowToType<T>(row));
}
return tableData;
}
public T ConvertRowToType<T>(DataRow row)
{
//??? What is the best thing to do here ???
}
如果任何人的建议需要更改,我不会注意这段代码。
所以让我说我把这个函数称为传递类型......
public class mdbConcern
{
public Int32 ConcernId { get; set; }
public String Concern { get; set; }
}
DataTable中的数据看起来像......
ConcernID Concern
1 Law and Ethics
2 Mail
3 Business English
... ...
实现ConvertRowToType(DataRow行)方法的最佳方法是什么?
有人可以告诉我如何使用Func作为其中一个参数,以便传递一些映射信息吗?
答案 0 :(得分:8)
我认为extension method
是最好的方式:
public static class Helper
{
public static T ToType<T>(this DataRow row) where T : new()
{
T obj = new T();
var props = TypeDescriptor.GetProperties(obj);
foreach (PropertyDescriptor prop in props)
{
if(row.Table.Columns.IndexOf(prop.Name) >= 0
&& row[prop.Name].GetType() == prop.PropertyType)
{
prop.SetValue(obj, row[prop.Name]);
}
}
return obj;
}
}
用法:
public List<T> ImportTable<T>(String fileName, String table)
{
//Establish Connection to Access Database File
var mdbData = new ConnectToAccess(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=F:\ACCESS\" + fileName + ".mdb;");
var tableData = new List<T>();
foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
tableData.Add(row.ToType<T>());
}
return tableData;
}
更新我看到您要求提供映射的Func
。我不确定你想象的是什么,但这是我想出的方法:
public class mdbConcern
{
public Int32 ConcernId { get; set; }
public String Concern { get; set; }
public static PropertyDescriptor Mapping(string name)
{
PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(mdbConcern));
switch (name)
{
case "Concern_Id":
return props.GetByName("ConcernId");
case "Concern":
return props.GetByName("Concern");
default:
return null;
}
}
}
public static class Helper
{
public static T ToType<T>(this DataRow row, Func<string, PropertyDescriptor> mapping)
where T : new()
{
T obj = new T();
foreach (DataColumn col in row.Table.Columns)
{
var prop = mapping(col.ColumnName);
if(prop != null)
prop.SetValue(obj, row[col]);
}
return obj;
}
}
用法:
foreach (DataRow row in mdbData.GetData("SELECT * FROM " + table).Rows)
{
tableData.Add(row.ToType<mdbConcern>(mdbConcern.Mapping));
}
这是一个使用类型属性的属性来存储其映射的版本。我认为这是一个更自然的解决方案:
[AttributeUsage(AttributeTargets.Property)]
public class ColumnMappingAttribute : Attribute
{
public string Name { get; set; }
public ColumnMappingAttribute(string name)
{
Name = name;
}
}
public class mdbConcern
{
ColumnMapping("Concern_Id")]
public Int32 ConcernId { get; set; }
ColumnMapping("Concern")]
public String Concern { get; set; }
}
public static class Helper
{
public static T ToType<T>(this DataRow row) where T : new()
{
T obj = new T();
var props = TypeDescriptor.GetProperties(obj);
foreach (PropertyDescriptor prop in props)
{
var columnMapping = prop.Attributes.OfType<ColumnMappingAttribute>().FirstOrDefault();
if(columnMapping != null)
{
if(row.Table.Columns.IndexOf(columnMapping.Name) >= 0
&& row[columnMapping.Name].GetType() == prop.PropertyType)
{
prop.SetValue(obj, row[columnMapping.Name]);
}
}
}
return obj;
}
}
答案 1 :(得分:0)
除了@Sorax的答案。我增强了ToType
方法以支持Nullable<>
类型成员(使用字段而不是属性和TypeInfo
而不是TypeDescriptor
)。它将整个DataTable
对象作为参数并返回IList
。
protected IList<TResult> TableToList<TResult>(DataTable table) where TResult : new()
{
var result = new List<TResult>(table.Rows.Count);
var fields = typeof(TResult).GetTypeInfo().DeclaredFields;
TResult obj;
Object colVal;
var columns = table.Columns;
var nullableTypeDefinition = typeof(Nullable<>);
var dbNullType = typeof(DBNull);
Type[] genericArguments;
foreach (DataRow row in table.Rows)
{
obj = new TResult();
foreach (var f in fields)
{
if (columns.Contains(f.Name))
{
colVal = row[f.Name];
if (colVal.GetType() == f.FieldType)
{
f.SetValue(obj, colVal);
}
else if (colVal.GetType() != dbNullType && f.FieldType.IsGenericType &&
f.FieldType.GetGenericTypeDefinition() == nullableTypeDefinition)
{
genericArguments = f.FieldType.GetGenericArguments();
if (genericArguments.Length > 0 && genericArguments[0] == colVal.GetType())
{
f.SetValue(obj, colVal);
}
}
}
}
result.Add(obj);
}
return result;
}