寫一個對象的屬性到DataTable我有這個簡單的模型的列表:通過表達式樹
// model:
public class Test {
public int ID { get; set; }
public string Name { get; set; }
}
爲var list = new List<Test>() { /* some items here */ };
。而我通過這個片段生成從list
一個DataTable
:
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;
}
但是,當我打電話.Compile()
方法(在塊的結束,只是return
之前)我得到這個錯誤:
An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code
Additional information: variable 'item' of type 'TestEntity' referenced from scope '', but it is not defined
你有什麼想法我在這裏錯過了什麼?提前致謝。乾杯。
UPDATE: 這裏是循環發電機:
public static class ExpressionHelper {
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;
}
}
'loopVariable'被定義爲一個參數,但是你永遠不會把它提供給你的lambda。你的意思是使用'Expression.Variable'嗎? – Rob
@Rob \t 我將'loopVariable'改爲'Expression.Variable',但問題存在。 –