public static class EntityConverter
{
public static Dictionary<string, Func<DataRow, object>> handleDict =
new Dictionary<string, Func<DataRow, object>>();
public static Dictionary<string, Func<IDataRecord, List<string>, object>> DataRecordHandler =
new Dictionary<string, Func<IDataRecord, List<string>, object>>();
/// <summary>
/// 将DataTable转为对象列表
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static List<T> ToList<T>(this DataTable dt, bool bIsDispose) where T : class, new()
{
List<T> list = new List<T>();
if (dt == null || dt.Rows.Count == 0)
return list;
Func<DataRow, object> handler = null;
string key = dt.TableName + "_" + typeof(T).Name;
if (handleDict.ContainsKey(key))
{
handler = (Func<DataRow, object>)handleDict[key];
}
else
{
DataTableEntityBuilder<T> eblist = DataTableEntityBuilder<T>.CreateBuilderOfDataRow();
handler = eblist.DataRowHandler;
handleDict.Add(key, handler);
}
try
{
foreach (DataRow info in dt.Rows)
{
list.Add((T)handler(info));
}
}
finally
{
if (bIsDispose)
{
dt.Dispose();
dt = null;
}
}
return list;
}
/// <summary>
/// 将DataTable转为对象列表
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static T ToEntity<T>(this DataRow dr) where T : class, new()
{
if (dr == null)
return null;
Func<DataRow, object> handler = null;
string key = dr.Table.TableName + "_" + typeof(T).Name;
if (handleDict.ContainsKey(key))
{
handler = (Func<DataRow, object>)handleDict[key];
}
else
{
DataTableEntityBuilder<T> eblist = DataTableEntityBuilder<T>.CreateBuilderOfDataRow();
handler = eblist.DataRowHandler;
handleDict.Add(key, handler);
}
try
{
return (T)handler(dr);
}
finally
{
}
}
/// <summary>
/// 将DataTable转为对象列表
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dt"></param>
/// <returns></returns>
public static List<T> ToList<T>(this IDataReader dr, bool bIsDispose) where T : class, new()
{
List<T> list = new List<T>();
if (dr == null)
return list;
Func<IDataRecord, List<string>, object> handler = null;
string key = "DataReader_" + typeof(T).Name;
if (DataRecordHandler.ContainsKey(key))
{
handler = (Func<IDataRecord, List<string>, object>)DataRecordHandler[key];
}
else
{
DataTableEntityBuilder<T> eblist = DataTableEntityBuilder<T>.CreateBuilderOfDataRecord();
handler = eblist.DataRecordHandler;
DataRecordHandler.Add(key, handler);
}
List<string> cols = new List<string>();
for (int i = 0; i < dr.FieldCount; i++)
{
cols.Add(dr.GetName(i).ToLower());
}
while (dr.Read())
{
list.Add((T)handler(dr, cols));
}
return list;
}
/// <summary>
/// 使用EMIT
/// </summary>
/// <typeparam name="Entity"></typeparam>
public class DataTableEntityBuilder<Entity>
{
//数据类型和对应的强制转换方法的methodinfo,供实体属性赋值时调用
private static Dictionary<Type, MethodInfo> ConvertMethods = new Dictionary<Type, MethodInfo>()
{
{typeof(int),typeof(Convert).GetMethod("ToInt32",new Type[]{typeof(object)})},
{typeof(Int16),typeof(Convert).GetMethod("ToInt16",new Type[]{typeof(object)})},
{typeof(Int64),typeof(Convert).GetMethod("ToInt64",new Type[]{typeof(object)})},
{typeof(DateTime),typeof(Convert).GetMethod("ToDateTime",new Type[]{typeof(object)})},
{typeof(decimal),typeof(Convert).GetMethod("ToDecimal",new Type[]{typeof(object)})},
{typeof(Double),typeof(Convert).GetMethod("ToDouble",new Type[]{typeof(object)})},
{typeof(Boolean),typeof(Convert).GetMethod("ToBoolean",new Type[]{typeof(object)})},
{typeof(string),typeof(Convert).GetMethod("ToString",new Type[]{typeof(object)})},
//{typeof(Nullable<int>),typeof(Convert).GetMethod("ToInt32",new Type[]{typeof(object)})},
//{typeof(Nullable<Int16>),typeof(Convert).GetMethod("ToInt16",new Type[]{typeof(object)})},
//{typeof(Nullable<Int64>),typeof(Convert).GetMethod("ToInt64",new Type[]{typeof(object)})},
//{typeof(Nullable<DateTime>),typeof(Convert).GetMethod("ToDateTime",new Type[]{typeof(object)})},
//{typeof(Nullable<decimal>),typeof(Convert).GetMethod("ToDecimal",new Type[]{typeof(object)})},
//{typeof(Nullable<Double>),typeof(Convert).GetMethod("ToDouble",new Type[]{typeof(object)})},
//{typeof(Nullable<Boolean>),typeof(Convert).GetMethod("ToBoolean",new Type[]{typeof(object)})},
//{typeof(string),typeof(Convert).GetMethod("ToString",new Type[]{typeof(object)})}
};
#region DataRow
private static readonly MethodInfo getValueMethod = typeof(DataRow).GetMethod("get_Item", new Type[] { typeof(string) });
private static readonly MethodInfo isDBNullMethod = typeof(DataRow).GetMethod("IsNull", new Type[] { typeof(string) });
private static readonly MethodInfo getTableMethod = typeof(DataRow).GetMethod("get_Table", new Type[] { });
private static readonly MethodInfo getColumns = typeof(DataTable).GetMethod("get_Columns", new Type[] { });
private static readonly MethodInfo Contains = typeof(DataColumnCollection).GetMethod("Contains", new Type[] { typeof(string) });
public Func<DataRow, object> DataRowHandler { get; set; }
#endregion
#region IDataRecord
private static readonly MethodInfo IndexOf = typeof(List<string>).GetMethod("IndexOf", new Type[] { typeof(string) });
private static readonly MethodInfo getCount = typeof(List<string>).GetMethod("get_Count", new Type[] { });
private static readonly MethodInfo IsDBNull = typeof(IDataRecord).GetMethod("IsDBNull", new Type[] { typeof(int) });
private static readonly MethodInfo getItem = typeof(IDataRecord).GetMethod("get_Item", new Type[] { typeof(int) });
public Func<IDataRecord, List<string>, object> DataRecordHandler { get; set; }
#endregion
private DataTableEntityBuilder() { }
/// <summary>
/// 如果DataRow中的列与实体中的不一致,会报错
/// </summary>
/// <param name="dataRow"></param>
/// <returns></returns>
public static DataTableEntityBuilder<Entity> CreateBuilder(DataRow dataRow)
{
DataTableEntityBuilder<Entity> dynamicBuilder = new DataTableEntityBuilder<Entity>();
DynamicMethod method = new DynamicMethod("DynamicCreateEntity", typeof(Entity), new Type[] { typeof(DataRow) }, typeof(Entity), true);
ILGenerator generator = method.GetILGenerator();
LocalBuilder result = generator.DeclareLocal(typeof(Entity));
generator.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, result);
for (int index = 0; index < dataRow.ItemArray.Length; index++)
{
PropertyInfo propertyInfo = typeof(Entity).GetProperty(dataRow.Table.Columns[index].ColumnName);
Label endIfLabel = generator.DefineLabel();
if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
{
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, index);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
generator.Emit(OpCodes.Brtrue, endIfLabel);
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldc_I4, index);
generator.Emit(OpCodes.Callvirt, getValueMethod);
generator.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
generator.MarkLabel(endIfLabel);
}
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
dynamicBuilder.DataRowHandler = (Func<DataRow, object>)method.CreateDelegate(typeof(Func<DataRow, object>));
return dynamicBuilder;
}
/// <summary>
/// DataRow转Entity
/// </summary>
/// <returns></returns>
public static DataTableEntityBuilder<Entity> CreateBuilderOfDataRow()
{
DataTableEntityBuilder<Entity> dynamicBuilder = new DataTableEntityBuilder<Entity>();
DynamicMethod method = new DynamicMethod("DataRow2Entity", typeof(Entity), new Type[] { typeof(DataRow) }, typeof(Entity), true);
ILGenerator generator = method.GetILGenerator();
// 返回值(如Role对象)
// T result;
LocalBuilder result = generator.DeclareLocal(typeof(Entity));
// result = new T();
generator.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, result);
foreach (PropertyInfo property in typeof(Entity).GetProperties())
{
// if (dr.Table.Columns.Contains(property.Name))
Label endIfContains = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Callvirt, getTableMethod);
generator.Emit(OpCodes.Callvirt, getColumns);
generator.Emit(OpCodes.Ldstr, property.Name);
generator.Emit(OpCodes.Callvirt, Contains);
// 为false,则到generator.MarkLabel(endIfContains);中的代码不执行
generator.Emit(OpCodes.Brfalse, endIfContains);
// if (!dr.IsNull(property.Name))
Label endIfDBNull = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldstr, property.Name);
generator.Emit(OpCodes.Callvirt, isDBNullMethod);
// 如果dr.IsNull(property.Name)为true,则到generator.MarkLabel(endIfDBNull)间的代码不执行
generator.Emit(OpCodes.Brtrue, endIfDBNull);
//generator.Emit(OpCodes.Ldstr, "I'm test");
//generator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) }));
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldstr, property.Name);
generator.Emit(OpCodes.Callvirt, getValueMethod);
bool bIsNullable = false;
Type columnType = property.PropertyType;
if (property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
bIsNullable = true;
// If it is NULLABLE, then get the underlying type. eg if "Nullable<int>" then this will return just "int"
columnType = property.PropertyType.GetGenericArguments()[0];
}
var castMethod = typeof(Convert).GetMethod("To" + columnType.Name, new Type[] { typeof(object) });
if (columnType == typeof(DateTime))
{
//generator.Emit(OpCodes.Unbox_Any, columnType);
//generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
if (bIsNullable)
{
generator.Emit(OpCodes.Call, castMethod);
generator.Emit(OpCodes.Newobj, property.PropertyType.GetConstructor(new[] { columnType }));
}
else
{
generator.Emit(OpCodes.Unbox_Any, columnType);
}
}
else
{
// 使用下面的方法时,日期类型会出错
if ((columnType.IsValueType ||
columnType == typeof(string))
&& ConvertMethods.ContainsKey(columnType))
{
//generator.Emit(OpCodes.Call, ConvertMethods[columnType]);
generator.Emit(OpCodes.Call, castMethod);
if (bIsNullable)
{
//https://stackoverflow.com/questions/21711255/unboxing-nullable-when-emitting-code-for-a-method-leaves-evaluation-stack-in-an
generator.Emit(OpCodes.Newobj, property.PropertyType.GetConstructor(new[] { columnType }));
}
}
else
generator.Emit(OpCodes.Castclass, columnType);
//generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
}
generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
generator.MarkLabel(endIfDBNull);
generator.MarkLabel(endIfContains);
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
dynamicBuilder.DataRowHandler = (Func<DataRow, object>)method.CreateDelegate(typeof(Func<DataRow, object>));
return dynamicBuilder;
}
/// <summary>
/// DataReader转Entity
/// </summary>
/// <returns></returns>
public static DataTableEntityBuilder<Entity> CreateBuilderOfDataRecord()
{
DataTableEntityBuilder<Entity> dynamicBuilder = new DataTableEntityBuilder<Entity>();
DynamicMethod method = new DynamicMethod("DataReader2Entity", typeof(Entity), new Type[] { typeof(IDataRecord), typeof(List<string>) }, typeof(Entity), true);
ILGenerator generator = method.GetILGenerator();
// 返回值(如Role对象)
// T result;
LocalBuilder result = generator.DeclareLocal(typeof(Entity));
LocalBuilder j = generator.DeclareLocal(typeof(int));
// result = new T();
generator.Emit(OpCodes.Newobj, typeof(Entity).GetConstructor(Type.EmptyTypes));
generator.Emit(OpCodes.Stloc, result);
foreach (PropertyInfo property in typeof(Entity).GetProperties())
{
//int j = cols.IndexOf(property.Name.ToLower());
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Ldstr, property.Name.ToLower());
generator.Emit(OpCodes.Callvirt, IndexOf);
generator.Emit(OpCodes.Stloc, j);
// if (j >= 0)
Label endIfBlt = generator.DefineLabel();
generator.Emit(OpCodes.Ldloc_S, j);
generator.Emit(OpCodes.Ldc_I4_0);
// 为false,则到generator.MarkLabel(endIfContains); 中的代码不执行
generator.Emit(OpCodes.Blt_S, endIfBlt);
// if (j < cols.Count)
Label endIfBge = generator.DefineLabel();
generator.Emit(OpCodes.Ldloc_S, j);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Callvirt, getCount);
generator.Emit(OpCodes.Bge_S, endIfBge);
// if (!dr.IsDBNull(j))
Label endIfDBNull = generator.DefineLabel();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldloc_S, j);
generator.Emit(OpCodes.Callvirt, IsDBNull);
// 如果dr.IsNull(property.Name)为true,则到generator.MarkLabel(endIfDBNull)间的代码不执行
generator.Emit(OpCodes.Brtrue, endIfDBNull);
//generator.Emit(OpCodes.Ldstr, "I'm test");
//generator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(String) }));
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldloc_S, j);
generator.Emit(OpCodes.Callvirt, getItem);
generator.Emit(OpCodes.Unbox_Any, property.PropertyType);
generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
// System.Nullable`1<int32>
// 使用下面的方法时,日期类型会出错
//if ((property.PropertyType.IsValueType ||
// property.PropertyType == typeof(string))
// && ConvertMethods.ContainsKey(property.PropertyType))
// generator.Emit(OpCodes.Call, ConvertMethods[property.PropertyType]);
//else
// generator.Emit(OpCodes.Castclass, property.PropertyType);
//generator.Emit(OpCodes.Callvirt, property.GetSetMethod());
generator.MarkLabel(endIfDBNull);
generator.MarkLabel(endIfBge);
generator.MarkLabel(endIfBlt);
}
generator.Emit(OpCodes.Ldloc, result);
generator.Emit(OpCodes.Ret);
dynamicBuilder.DataRecordHandler = (Func<IDataRecord, List<string>, object>)method.CreateDelegate(typeof(Func<IDataRecord, List<string>, object>));
return dynamicBuilder;
}
}
/// <summary>
/// 实体转DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public static DataTable ToDataTable<T>(this IList<T> list)
{
Type elementType = typeof(T);
var t = new DataTable();
elementType.GetProperties().ToList().ForEach(propInfo => t.Columns.Add(propInfo.Name, Nullable.GetUnderlyingType(propInfo.PropertyType) ?? propInfo.PropertyType));
foreach (T item in list)
{
var row = t.NewRow();
elementType.GetProperties().ToList().ForEach(propInfo => row[propInfo.Name] = propInfo.GetValue(item, null) ?? DBNull.Value);
t.Rows.Add(row);
}
return t;
}
/// <summary>
/// 实体转DataTable
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="dt"></param>
public static void ToDataTable<T>(this IList<T> list, DataTable dt)
{
Type elementType = typeof(T);
foreach (T item in list)
{
var row = dt.NewRow();
elementType.GetProperties().ToList().ForEach(propInfo =>
{
if (dt.Columns.Contains(propInfo.Name))
row[propInfo.Name] = propInfo.GetValue(item, null) ?? DBNull.Value;
});
dt.Rows.Add(row);
}
}
}