C# 写一个对象';通过表达式树将属性设置为DataTable
我有一个简单模型的列表:C# 写一个对象';通过表达式树将属性设置为DataTable,c#,c#-4.0,lambda,expression-trees,C#,C# 4.0,Lambda,Expression Trees,我有一个简单模型的列表: // model: public class Test { public int ID { get; set; } public string Name { get; set; } } asvar list=newlist(){/*此处的某些项*/}。我正在从列表通过以下代码片段生成一个数据表: var dataTable = new DataTable(); dataTable.Columns.Add("ID", typeof(int)); data
// model:
public class Test {
public int ID { get; set; }
public string Name { get; set; }
}
asvar list=newlist(){/*此处的某些项*/}代码>。我正在从列表
通过以下代码片段生成一个数据表
:
var dataTable = new DataTable();
dataTable.Columns.Add("ID", typeof(int));
dataTable.Columns.Add("Name", typeof(string));
foreach (var item in list) {
var dr = dataTable.NewRow();
dr["ID"] = item.ID;
dr["Name"] = item.Name;
dataTable.Rows.Add(dr);
}
现在,我正在尝试生成一些表达式树,以便在运行时(以通用方式)执行上述代码段。然而,我的尝试让我走到了这里:
private static Action<DataTable, IEnumerable<T>> GetAction() {
if (_filler != null)
return;
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var tableParam = Expression.Parameter(typeof(DataTable), "targetTable");
var rowsParam = Expression.Parameter(typeof(IEnumerable<T>), "rows");
var loopVariable = Expression.Parameter(typeof(T), "item");
var columnsVariable = Expression.Parameter(typeof(DataColumnCollection), "columns");
var columnsAssign = Expression.Assign(columnsVariable,
Expression.Property(tableParam, typeof(DataTable).GetProperty("Columns")));
var headerExpressions = new List<Expression>();
var bodyExpressions = new List<Expression>();
var newRowParam = Expression.Parameter(typeof(DataRow), "currentRow");
var newRowAssign = Expression.Assign(newRowParam, Expression.Call(tableParam, typeof(DataTable).GetMethod("NewRow")));
foreach (var prop in props) {
var getMethod = prop.GetGetMethod(false);
if (getMethod == null)
continue;
var attr = prop.GetCustomAttribute<UdtColumnAttribute>();
var name = attr == null ? prop.Name : attr.ColumnName;
var headerNameParam = Expression.Parameter(typeof(string), "columnName");
var headerNameAssign = Expression.Assign(headerNameParam, Expression.Constant(name, typeof(string)));
var headerTypeParam = Expression.Parameter(typeof(Type), "columnType");
var headerTypeAssign = Expression.Assign(headerTypeParam, Expression.Constant(prop.PropertyType, typeof(Type)));
var columnsAddMethod = Expression.Call(columnsVariable,
typeof(DataColumnCollection).GetMethod("Add", new[] { typeof(string), typeof(Type) }),
headerNameParam, headerTypeParam);
headerExpressions.AddRange(new Expression[] {
headerNameParam,
headerNameAssign,
headerTypeParam,
headerTypeAssign,
columnsAddMethod,
});
var indexerProp = typeof(DataRow).GetProperty("Item", new[] { typeof(string) });
var indexerParam = Expression.Property(newRowParam, indexerProp, Expression.Constant(name, typeof(string)));
var propertyReaderMethod = Expression.Call(loopVariable, getMethod);
var assign = Expression.Assign(indexerParam, Expression.TypeAs(propertyReaderMethod, typeof(object)));
bodyExpressions.AddRange(new Expression[] { indexerParam, propertyReaderMethod, assign });
}
var finalExpressions = new List<Expression>() {
tableParam,
rowsParam,
loopVariable,
columnsVariable,
columnsAssign,
newRowParam,
newRowAssign,
};
finalExpressions.AddRange(headerExpressions);
var loop = ExpressionHelper.ForEach(rowsParam, loopVariable, Expression.Block(bodyExpressions));
finalExpressions.Add(loop);
var compilable = Expression.Block(finalExpressions);
var code = compilable.ToString();
Trace.WriteLine(code);
var compiled = Expression.Lambda<Action<DataTable, IEnumerable<T>>>(compilable, tableParam, rowsParam).Compile();
return compiled;
}
更新后的工作代码如下,DotNetFiddle中的工作示例为-
最初,您的代码存在以下两个问题:
主体表达式应该有单独的变量和实际表达式。在示例中,您正在其他参数中添加ExpressionParameter
,并将它们传递给Body
call。但它们应该单独通过。因此,必须用变量列表传递第一个参数,用实际表达式传递第二个参数
循环代码缺少实际表达式var dr=dataTable.NewRow()代码>是您生成的,但未添加到循环中的。它还错过了上次调用dataTable.Rows.Add(dr)代码>已填充行需要添加回行
在我的示例中,我修复了这两个问题,现在代码根据Test
实体列表填充DataTable
public class Program
{
static void Main(string[] args)
{
var data = new List<Test>()
{
new Test() {ID = 1, Name = "1Text"},
new Test() {ID = 2, Name = "2Text"},
};
var action = ExpressionHelper.GetAction<Test>();
var dataTable = new DataTable();
action(dataTable, data);
foreach (DataRow row in dataTable.Rows)
{
Console.WriteLine($"ID: {row["ID"]}, Name: {row["Name"]}");
}
}
}
public class ExpressionHelper
{
public static Action<DataTable, IEnumerable<T>> GetAction<T>()
{
//if (_filler != null)
// return null;
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var tableParam = Expression.Parameter(typeof(DataTable), "targetTable");
var rowsParam = Expression.Parameter(typeof(IEnumerable<T>), "rows");
var loopVariable = Expression.Parameter(typeof(T), "item");
var columnsVariable = Expression.Parameter(typeof(DataColumnCollection), "columns");
var columnsAssign = Expression.Assign(columnsVariable,
Expression.Property(tableParam, typeof(DataTable).GetProperty("Columns")));
var headerExpressions = new List<Expression>();
var bodyExpressions = new List<Expression>();
var headerNameParam = Expression.Parameter(typeof(string), "columnName");
var headerTypeParam = Expression.Parameter(typeof(Type), "columnType");
var newRowParam = Expression.Parameter(typeof(DataRow), "currentRow");
var newRowAssign = Expression.Assign(newRowParam, Expression.Call(tableParam, typeof(DataTable).GetMethod("NewRow")));
bodyExpressions.Add(newRowAssign);
foreach (var prop in props)
{
var getMethod = prop.GetGetMethod(false);
if (getMethod == null)
continue;
var attr = prop.GetCustomAttribute<UdtColumnAttribute>();
var name = attr == null ? prop.Name : attr.ColumnName;
var headerNameAssign = Expression.Assign(headerNameParam, Expression.Constant(name, typeof(string)));
var headerTypeAssign = Expression.Assign(headerTypeParam, Expression.Constant(prop.PropertyType, typeof(Type)));
var columnsAddMethod = Expression.Call(columnsVariable,
typeof(DataColumnCollection).GetMethod("Add", new[] { typeof(string), typeof(Type) }),
headerNameParam, headerTypeParam);
headerExpressions.AddRange(new Expression[] {
headerNameAssign,
headerTypeAssign,
columnsAddMethod,
});
var indexerProp = typeof(DataRow).GetProperty("Item", new[] { typeof(string) });
var indexerParam = Expression.Property(newRowParam, indexerProp, Expression.Constant(name, typeof(string)));
var propertyReaderMethod = Expression.Call(loopVariable, getMethod);
var assign = Expression.Assign(indexerParam, Expression.TypeAs(propertyReaderMethod, typeof(object)));
bodyExpressions.AddRange(new Expression[] { assign });
}
// we should add that row back to collection
var addRow = Expression.Call(
Expression.Property(tableParam, "Rows"),
typeof(DataRowCollection).GetMethod("Add", new Type[] {typeof(DataRow)}),
newRowParam);
bodyExpressions.Add(addRow);
var finalExpressions = new List<Expression>()
{
columnsAssign,
newRowAssign,
};
var variables = new List<ParameterExpression>()
{
loopVariable,
columnsVariable,
newRowParam,
headerNameParam,
headerTypeParam
};
finalExpressions.AddRange(headerExpressions);
var loop = ExpressionHelper.ForEach(rowsParam, loopVariable, Expression.Block(bodyExpressions));
finalExpressions.Add(loop);
var compilable = Expression.Block(variables, finalExpressions);
var code = compilable.ToString();
Trace.WriteLine(code);
var compiled = Expression.Lambda<Action<DataTable, IEnumerable<T>>>(compilable, tableParam, rowsParam).Compile();
return compiled;
}
public static Expression ForEach(Expression collection, ParameterExpression loopVar, Expression loopContent)
{
var elementType = loopVar.Type;
var enumerableType = typeof(IEnumerable<>).MakeGenericType(elementType);
var enumeratorType = typeof(IEnumerator<>).MakeGenericType(elementType);
var enumeratorVar = Expression.Variable(enumeratorType, "enumerator");
var getEnumeratorCall = Expression.Call(collection, enumerableType.GetMethod("GetEnumerator"));
var enumeratorAssign = Expression.Assign(enumeratorVar, getEnumeratorCall);
// The MoveNext method's actually on IEnumerator, not IEnumerator<T>
var moveNextCall = Expression.Call(enumeratorVar, typeof(IEnumerator).GetMethod("MoveNext"));
var breakLabel = Expression.Label("LoopBreak");
var loop = Expression.Block(new[] { enumeratorVar },
enumeratorAssign,
Expression.Loop(
Expression.IfThenElse(
Expression.Equal(moveNextCall, Expression.Constant(true)),
Expression.Block(new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumeratorVar, "Current")),
loopContent
),
Expression.Break(breakLabel)
),
breakLabel)
);
return loop;
}
}
public class Test
{
public int ID { get; set; }
public string Name { get; set; }
}
public class UdtColumnAttribute : Attribute
{
public string ColumnName { get; set; }
}
公共类程序
{
静态void Main(字符串[]参数)
{
变量数据=新列表()
{
新测试(){ID=1,Name=“1Text”},
新测试(){ID=2,Name=“2Text”},
};
var action=ExpressionHelper.GetAction();
var dataTable=新的dataTable();
动作(数据表、数据);
foreach(dataTable.Rows中的DataRow行)
{
WriteLine($“ID:{row[“ID”]},Name:{row[“Name”]}”);
}
}
}
公共类ExpressionHelper
{
公共静态操作GetAction()
{
//如果(_filler!=null)
//返回null;
var类型=类型(T);
var props=type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var tableParam=Expression.Parameter(typeof(DataTable),“targetTable”);
var rowsParam=Expression.Parameter(typeof(IEnumerable),“rows”);
var loopVariable=表达式参数(类型(T),“项”);
var columnsVariable=Expression.Parameter(typeof(DataColumnCollection),“columns”);
var columnsAssign=Expression.Assign(columnsVariable,
Expression.Property(tableParam,typeof(DataTable.GetProperty(“Columns”));
var headerExpressions=新列表();
var bodyExpressions=新列表();
var headerNameParam=Expression.Parameter(typeof(string),“columnName”);
var headerTypeParam=表达式.参数(typeof(Type),“columnType”);
var newRowParam=Expression.Parameter(typeof(DataRow),“currentRow”);
var newrowsign=Expression.Assign(newRowParam,Expression.Call(tableParam,typeof(DataTable.GetMethod)(“NewRow”));
bodyExpressions.Add(newrowsign);
foreach(道具中的var道具)
{
var getMethod=prop.getMethod(false);
if(getMethod==null)
继续;
var attr=prop.GetCustomAttribute();
var name=attr==null?属性名称:attr.ColumnName;
var headerNameAssign=Expression.Assign(headerNameParam,Expression.Constant(name,typeof(string));
var headerTypeAssign=Expression.Assign(headerTypeParam,Expression.Constant(prop.PropertyType,typeof(Type));
var columnsAddMethod=Expression.Call(columnsVariable,
typeof(DataColumnCollection).GetMethod(“添加”,新[]{typeof(字符串),typeof(类型)}),
headerNameParam、headerTypeParam);
headerExpressions.AddRange(新表达式[]){
头像标志,
校长类型分配,
柱状法,
});
var indexerProp=typeof(DataRow).GetProperty(“Item”,new[]{typeof(string)});
var indexerParam=Expression.Property(newRowParam,indexerProp,Expression.Constant(name,typeof(string));
var propertyReaderMethod=Expression.Call(loopVariable,getMethod);
var assign=Expression.assign(indexerParam,Expression.TypeAs(propertyReaderMethod,typeof(object));
AddRange(新表达式[]{assign});
}
//我们应该将该行添加回集合
var addRow=Expression.Call(
Expression.Property(tableParam,“行”),
typeof(DataRowCollection).GetMethod(“Add”,新类型[]{typeof(DataRow)}),
newRowParam);
bodyExpressions.Add(addRow);
var finalExpressions=新列表()
{
柱状符号,
纽罗,
};
var变量=新列表()
{
循环变量,
列变量,
newRowParam,
海德纳梅帕拉姆,
headerTypeParam
};
最终表达式.AddRange(headerExpressions);
var loop=ExpressionHelper.ForEach(rowsParam,loopVariable,Expression.Block(bodyExpressions));
添加(循环);
var compileable=Expression.Block(变量、finalExpressions);
var code=compileable.ToString();
Trace.WriteLine(代码);
var compiled=Expression.Lambda(可编译,tableParam,rowsParam).Compile();
编制报表;
}
公共静态表达式ForEach(表达式集合、参数Expression loopVar、表达式loopContent)
{
var elementType=loopVar.Type;
var enumerableType=typeof(IEnumerable);
var enumeratorType=typeof(IEnumerator)。MakeGenericType(elementType);
var enumeratorVar=Expression.Variable(enumeratorType,“枚举器”);
var getEnumeratorCall=Expressi
public class Program
{
static void Main(string[] args)
{
var data = new List<Test>()
{
new Test() {ID = 1, Name = "1Text"},
new Test() {ID = 2, Name = "2Text"},
};
var action = ExpressionHelper.GetAction<Test>();
var dataTable = new DataTable();
action(dataTable, data);
foreach (DataRow row in dataTable.Rows)
{
Console.WriteLine($"ID: {row["ID"]}, Name: {row["Name"]}");
}
}
}
public class ExpressionHelper
{
public static Action<DataTable, IEnumerable<T>> GetAction<T>()
{
//if (_filler != null)
// return null;
var type = typeof(T);
var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
var tableParam = Expression.Parameter(typeof(DataTable), "targetTable");
var rowsParam = Expression.Parameter(typeof(IEnumerable<T>), "rows");
var loopVariable = Expression.Parameter(typeof(T), "item");
var columnsVariable = Expression.Parameter(typeof(DataColumnCollection), "columns");
var columnsAssign = Expression.Assign(columnsVariable,
Expression.Property(tableParam, typeof(DataTable).GetProperty("Columns")));
var headerExpressions = new List<Expression>();
var bodyExpressions = new List<Expression>();
var headerNameParam = Expression.Parameter(typeof(string), "columnName");
var headerTypeParam = Expression.Parameter(typeof(Type), "columnType");
var newRowParam = Expression.Parameter(typeof(DataRow), "currentRow");
var newRowAssign = Expression.Assign(newRowParam, Expression.Call(tableParam, typeof(DataTable).GetMethod("NewRow")));
bodyExpressions.Add(newRowAssign);
foreach (var prop in props)
{
var getMethod = prop.GetGetMethod(false);
if (getMethod == null)
continue;
var attr = prop.GetCustomAttribute<UdtColumnAttribute>();
var name = attr == null ? prop.Name : attr.ColumnName;
var headerNameAssign = Expression.Assign(headerNameParam, Expression.Constant(name, typeof(string)));
var headerTypeAssign = Expression.Assign(headerTypeParam, Expression.Constant(prop.PropertyType, typeof(Type)));
var columnsAddMethod = Expression.Call(columnsVariable,
typeof(DataColumnCollection).GetMethod("Add", new[] { typeof(string), typeof(Type) }),
headerNameParam, headerTypeParam);
headerExpressions.AddRange(new Expression[] {
headerNameAssign,
headerTypeAssign,
columnsAddMethod,
});
var indexerProp = typeof(DataRow).GetProperty("Item", new[] { typeof(string) });
var indexerParam = Expression.Property(newRowParam, indexerProp, Expression.Constant(name, typeof(string)));
var propertyReaderMethod = Expression.Call(loopVariable, getMethod);
var assign = Expression.Assign(indexerParam, Expression.TypeAs(propertyReaderMethod, typeof(object)));
bodyExpressions.AddRange(new Expression[] { assign });
}
// we should add that row back to collection
var addRow = Expression.Call(
Expression.Property(tableParam, "Rows"),
typeof(DataRowCollection).GetMethod("Add", new Type[] {typeof(DataRow)}),
newRowParam);
bodyExpressions.Add(addRow);
var finalExpressions = new List<Expression>()
{
columnsAssign,
newRowAssign,
};
var variables = new List<ParameterExpression>()
{
loopVariable,
columnsVariable,
newRowParam,
headerNameParam,
headerTypeParam
};
finalExpressions.AddRange(headerExpressions);
var loop = ExpressionHelper.ForEach(rowsParam, loopVariable, Expression.Block(bodyExpressions));
finalExpressions.Add(loop);
var compilable = Expression.Block(variables, finalExpressions);
var code = compilable.ToString();
Trace.WriteLine(code);
var compiled = Expression.Lambda<Action<DataTable, IEnumerable<T>>>(compilable, tableParam, rowsParam).Compile();
return compiled;
}
public static Expression ForEach(Expression collection, ParameterExpression loopVar, Expression loopContent)
{
var elementType = loopVar.Type;
var enumerableType = typeof(IEnumerable<>).MakeGenericType(elementType);
var enumeratorType = typeof(IEnumerator<>).MakeGenericType(elementType);
var enumeratorVar = Expression.Variable(enumeratorType, "enumerator");
var getEnumeratorCall = Expression.Call(collection, enumerableType.GetMethod("GetEnumerator"));
var enumeratorAssign = Expression.Assign(enumeratorVar, getEnumeratorCall);
// The MoveNext method's actually on IEnumerator, not IEnumerator<T>
var moveNextCall = Expression.Call(enumeratorVar, typeof(IEnumerator).GetMethod("MoveNext"));
var breakLabel = Expression.Label("LoopBreak");
var loop = Expression.Block(new[] { enumeratorVar },
enumeratorAssign,
Expression.Loop(
Expression.IfThenElse(
Expression.Equal(moveNextCall, Expression.Constant(true)),
Expression.Block(new[] { loopVar },
Expression.Assign(loopVar, Expression.Property(enumeratorVar, "Current")),
loopContent
),
Expression.Break(breakLabel)
),
breakLabel)
);
return loop;
}
}
public class Test
{
public int ID { get; set; }
public string Name { get; set; }
}
public class UdtColumnAttribute : Attribute
{
public string ColumnName { get; set; }
}
public class PropHelper
{
public PropertyInfo PropInfo {get;set;}
public Func<object, object> Getter {get;set;}
}
private static readonly ConcurrentDictionary<Type, IEnumerable<PropHelper>> s_cachedPropHelpers = new ConcurrentDictionary<Type, IEnumerable<PropHelper>>();
public static IEnumerable<PropHelper> GetPropHelpers(Type type)
{
return s_cachedPropHelpers.GetOrAdd(type, t =>
{
var props = t.GetProperties();
var result = new List<PropHelper>();
var parameter = Expression.Parameter(typeof(object));
foreach(var prop in props)
{
result.Add(new PropHelper
{
PropInfo = prop,
Getter = Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.MakeMemberAccess(
Expression.Convert(parameter, t),
prop),
typeof(object)),
parameter).Compile(),
});
}
return result;
});
}
private static Action<DataTable, IEnumerable<T>> GetAction<T>()
{
return (dataTable, list) => {
var props = GetPropHelpers(typeof(T));
foreach(var prop in props)
dataTable.Columns.Add(prop.PropInfo.Name, prop.PropInfo.PropertyType);
foreach (var item in list)
{
var dr = dataTable.NewRow();
foreach(var prop in props)
dr[prop.PropInfo.Name] = prop.Getter(item);
dataTable.Rows.Add(dr);
}
};
}