用c#构建LINQ动态表达式

本文关键字:动态 表达式 LINQ 构建 | 更新日期: 2023-09-27 18:11:01

我想动态地构建linq表达式,在以下Select语句中使用从DB填充的数据表:

IQueryable iq = myDataTable
    .AsEnumerable()
    .AsQueryable()
    .Select(
        d => new {
            Field1 = d.Field<int>(37),
            Field2 = d.Field<string>(0),
            Field3 = d.Field<DateTime>(1)
            }
    );

字段在运行时之前是未知的。我将在运行时获得字段列表("Field1","Field2","Field3"…),它们的类型(int, string, datetime…)和它们的索引(37,0,1…)。

有什么方法可以做到这一点吗?

(更新)

我开始写下面的代码:
Type type = typeof(DataRow);
MethodInfo mi1 = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(int) });
mi1 = mi1.MakeGenericMethod(type);
List<Expression> list1 = new List<Expression>();
ParameterExpression datarow1 = Expression.Parameter(type, "datarow");
ConstantExpression constant1 = Expression.Constant(37, typeof(int));
list1.Add(datarow1);
list1.Add(constant1);
ReadOnlyCollection<Expression> collection1 = new ReadOnlyCollection<Expression>(list1);
MethodCallExpression right1 = Expression.Call(null, mi1, datarow1, constant1);
Expression left1 = Expression.Parameter(type, "Field1");
var expression1 = Expression.Assign(left1, right1);
MethodInfo mi2 = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(int) });
mi2 = mi2.MakeGenericMethod(type);
List<Expression> list2 = new List<Expression>();
ParameterExpression datarow2 = Expression.Parameter(type, "datarow");
ConstantExpression constant2 = Expression.Constant(0, typeof(int));
list2.Add(datarow2);
list2.Add(constant2);
ReadOnlyCollection<Expression> collection2 = new ReadOnlyCollection<Expression>(list2);
MethodCallExpression right2 = Expression.Call(null, mi2, datarow2, constant2);
Expression left2 = Expression.Parameter(type, "Field2");
var expression2 = Expression.Assign(left2, right2);
List<Expression> bindings = new List<Expression>();
bindings.Add(expression1);
bindings.Add(expression2);
var newInit = Expression.New(typeof(object));
var init = Expression.NewArrayInit(type, bindings);
var dr = Expression.Parameter(type, "dr");
var linq = Expression.Lambda<Func<DataRow, DataRow>>(init, dr);

但是,它编译时有几个问题:

  • expression1是(Field1 = datarow.Field(0))而不是Field1 = datarow.Field<int>(0)
  • 表达2相同
  • 抛出一个运行时异常:'System.Data.DataRow[]' cannot be used for return type 'System.Data.DataRow'var linq = Expression.Lambda...

你能帮帮忙吗?

@George我认为我不需要类型字典,因为我有一组预定义的类型。

用c#构建LINQ动态表达式

对于这种情况,我强烈建议构建一个查询字符串并在数据库中运行它。

但是…为了艺术,你可以这样做。要实现您想要的效果,您需要的不仅仅是创建一个表达式树。首先,您将需要一个匿名类型的"池",用于在运行时找到的每个投影用例。当然,你需要有一本字典<somekey,>其中somekey可以是一个字符串,表示列(列索引等)的序列化组合,sometype将是一个真正的System.Type。但是等一下,这些类型在编译时不存在。问题是:如何创建这些类型?唯一的解决方案是使用反射。Emit和AssemblyBuilder、TypeBuilder、ModuleBuilder等…以便在运行时创建新类型。信不信由你,匿名类型只是普通的类型,但是c#编译器为我们做了这个神奇的工作。您还需要为数据表中的每个列和新类型中定义的每个属性提供映射。

1)所以……最终,您将针对您的特定情况请求"匿名类型"。如果该类型当前未构建,请先构建它,然后将其存储在字典中,然后……使用它。

2)剩下的工作是发出选择体,这实际上很简单。使用表达式。MemberInit和Expression。绑定的API。在谷歌上搜索他们,你会发现大量的信息。来了解使用表达式"绑定"什么。绑定后,在第二个字典中搜索(列和属性之间的映射)。

希望这对你有帮助。我不能给你代码,因为我是从一个设备,我没有visual studio。