使用纯LINQ动态连接表

时间:2016-11-20 10:17:08

标签: c# entity-framework linq

我有以下情况:

  1. 可以有两个随机表,每个表超过100列。
  2. 其中一个表有另一个外键。
  3. 用户从两个表中选择一组列,我们应该从db中选择并作为JSON对象发回。限制是它应该是纯LINQ(而不是DynamicLINQ)。
  4. 我尝试用我能找到的答案来解决表达式,但我所取得的最好成绩是IEnumerable,这是不可接受的,因为我需要IQueriable来过滤它。我来回搜索,但我找到的唯一可行的变体是DynamicSQL here,但我不允许使用它。

    非常感谢任何想法。

    更新:作为一个例子,我有两个由fk连接的随机表,所以它只是一个常规连接,如from t1 in Table1 join t2 in Table2 on t1.field1 = t2.field2。我需要的是能够将select表达式传递给此连接,基于包含我想要选择的列的字符串集合构建,例如,如果我有{"t1.field1", "t1.field2", "t2.field3"},则连接应该看起来像{{1} }。

1 个答案:

答案 0 :(得分:0)

从列列表创建实例的问题在于,由于.NET是类型安全的,因此您需要一个可以实例化的类型。当您使用匿名类(即没有类名的new关键字)时,编译器将为您创建一个类。它是匿名的,但在编译时它仍然存在 (你可以在你选择的反编译器中检查它)。

如果你想在运行时真正完全动态 ,你必须在执行时动态创建和编译你的类。为此,您可能需要查看System.CodeDomSystem.Reflection.Emit命名空间,它们都包含允许您在运行时动态创建类型的类。然而,这将是一项非常艰巨的任务,它怀疑值得您的时间。然后你想访问这些对象中的数据,所以你可能不得不去找dynamic个变量。

更可行的是在编译时创建一个常规类并在LINQ查询中实例化它。此类将包含您可以设置的所有可能的属性。实例化它时,您不必填写所有字段。

一旦有了类,就可以动态创建将实例化它的表达式。这就是System.Linq.Expressions命名空间中的类的用途。 Expression类包含允许您创建所需表达式树的工厂方法。

要创建表达式树,首先必须分解要建模的表达式。 Join的Yor表达式看起来与此类似(假设您的容器类名为DataContainer):

(t1, t2) => new DataContainer {
    Value1 = t1.field1,
    Value2 = t1.field2,
    Value3 = t2.field3
}

此表达式必须根据其优先级在其各个部分中拆分:

  • =>
  • 创建的lambda运算符LambdaExpressionExpression.Lambda启动的lambda表达式
  • lambda运算符左侧的参数t1t2ParameterExpression,使用Expression.Parameter
  • 创建
  • lambda运算符右侧的对象实例化new DataContainerNewExpression,使用Expression.New
  • 创建
  • =创建的初始化块内的分配:BinaryExpression,使用Expression.Assign
  • 创建
  • 作业左侧的属性:MemberExpression,使用Expression.Property
  • 创建
  • 使用.field1
  • 创建的MemberExpressionExpression.Property解除引用的属性
  • 使用t1创建的参数访问ParameterExpressionExpression.Parameter(但您正在重复使用为lambda表达式创建的参数表达式)

正如您所看到的,与仅写下表达式(或使用Dynamic LINQ)相比,这非常繁琐。我将用子表达式t1.field1

来举例说明这一点

此时您将为lambda的左侧创建t1参数:

ParameterExpression t1Param = Expression.Parameter(typeof(Table1), "t1");

您重复使用的属性访问权限:

MemberExpression t1field1Property = Expression.Property(t1Param, "field1");

您在创建作业时使用的表达式,您将与实例化表达式中的其他作业一起使用,该表达式将用于lamdba表达式的右侧,以及其他所需的表达式。您也可以将其写为单个树(除了要重用的参数表达式之外)。

快乐的编码!