gpt4 book ai didi

C# 使用 ExpressionTree 将 DataTable 映射到 List

转载 作者:太空宇宙 更新时间:2023-11-03 23:08:52 25 4
gpt4 key购买 nike

我编写了一个 ToList(); 扩展方法来将 DataTable 转换为 List。这只在某些情况下有效,但我们有很多使用数据表的旧代码,有时需要它。我的问题是这种方法与反射一起工作是好的,但不是那个性能。对于 100.000 个数据行,我需要大约 1,2sek。

所以我决定用表达式树来构建它。起初我想替换属性的 Setter 调用。到目前为止,我可以很容易地获得值(value):

var exactType = Nullable.GetUnderlyingType(propType) ?? propType;
var wert = Convert.ChangeType(zeile[spaltenname], exactType);

并设置它:

propertyInfo.SetValue(tempObjekt, wert, null);

现在我搜索了 StackOverflow 并找到了这个:

var zielExp = Expression.Parameter(typeof(T));
var wertExp = Expression.Parameter(propType);

var propertyExp = Expression.Property(zielExp, matchProp);
var zuweisungExp = Expression.Assign(propertyExp, wertExp);

var setter = Expression.Lambda<Action<T, int>>(zuweisungExp, zielExp, wertExp).Compile();
setter(tempObjekt, wert);

我的大问题是 Lambda 操作需要一个整数。但我需要这个期待我的属性(property)的类型。我通过 PropertyInfo 获得了我的属性(property)类型。但是不能让它工作。以为我可以轻松制作:

Action<T, object>

但这会导致以下异常:

ArgumentException The ParameterExpression from Type "System.Int32" cannot be used as Delegateparameter from Type "System.Object".

有人知道可能的解决方案吗?

最佳答案

而不是通用的 Expression.Lambda您可以使用的方法 this overload它需要一个类型:

public static LambdaExpression Lambda(
Type delegateType,
Expression body,
params ParameterExpression[] parameters
)

然后你可以使用Type.MakeGenericType为您的操作创建类型的方法:

var actionType = typeof(Action<,>).MakeGenericType(typeof(T), proptype);
var setter = Expression.Lambda(actionType, zuweisungExp, zielExp, wertExp).Compile();

根据有关性能的评论进行编辑:

您也可以只构建表达式运行时来映射 DataTable到你的类(class) T有一个select,所以只需要使用一次反射,这应该会大大提高性能。我写了下面的扩展方法来转换 DataTableList<T> (请注意,如果您不打算将所有数据列映射到类中的属性,此方法抛出运行时异常,因此如果可能发生这种情况,请务必注意):

public static class LocalExtensions
{
public static List<T> DataTableToList<T>(this DataTable table) where T : class
{
//Map the properties in a dictionary by name for easy access
var propertiesByName = typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(p => p.Name);
var columnNames = table.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName);

//The indexer property to access DataRow["columnName"] is called "Item"
var property = typeof(DataRow).GetProperties().First(p => p.Name == "Item"
&& p.GetIndexParameters().Length == 1
&& p.GetIndexParameters()[0].ParameterType == typeof(string));

var paramExpr = Expression.Parameter(typeof(DataRow), "r");
var newExpr = Expression.New(typeof(T));

//Create the expressions to map properties from your class to the corresponding
//value in the datarow. This will throw a runtime exception if your class
//doesn't contain properties for all columnnames!
var memberBindings = columnNames.Select(columnName =>
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
//Datarow["columnName"] is of type object, cast to the right type
var convert = Expression.Convert(indexExpr, pi.PropertyType);

return Expression.Bind(pi, convert);
});
var initExpr = Expression.MemberInit(newExpr, memberBindings);
var func = Expression.Lambda<Func<DataRow, T>>(initExpr,paramExpr).Compile();

return table.Rows.Cast<DataRow>().Select(func).ToList();
}
}

然后我编写了一个小测试类和一些代码,这些代码创建了一个包含 1,000,000 行的数据表,这些行被映射到一个列表。构建表达式 + 转换为列表现在在我的电脑上只需要 486 毫秒(当然这是一个非常小的类):

class Test
{
public string TestString { get; set; }
public int TestInt { get; set; }
}

class Program
{
static void Main()
{
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("TestString", typeof(string)));
table.Columns.Add(new DataColumn("TestInt", typeof(int)));

for(int i = 0; i < 1000000; i++)
{
var row = table.NewRow();
row["TestString"] = $"String number: {i}";
row["TestInt"] = i;
table.Rows.Add(row);
}

var stopwatch = Stopwatch.StartNew();

var myList = table.DataTableToList<Test>();

stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
}
}

关于C# 使用 ExpressionTree 将 DataTable 映射到 List<T>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40156492/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com