【问题标题】:Joining tables dynamically with pure LINQ使用纯 LINQ 动态连接表
【发布时间】:2016-11-22 17:03:52
【问题描述】:

我有以下场景:

  1. 可以有两个随机表,每个表超过 100 列。
  2. 其中一个表具有另一个表的外键。
  3. 用户从两个表中选择一组列,我们应该从 db 中选择并作为 JSON 对象发回。限制是它应该是纯 LINQ(而不是 DynamicLINQ)。

我试图通过我能找到的答案来玩弄表达式,但我取得的最好结果是 IEnumerable 作为结果,这是不可接受的,因为我需要 IQueriable 稍后对其进行过滤。我来回搜索,但我发现的唯一工作变体是 DynamicSQL here,但我不允许使用它。

非常感谢任何想法。

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

【问题讨论】:

  • 将您的联接写入 db 作为视图。并将此视图映射到您的实体。
  • @user1681317,正如我所说,会有随机表和随机列集,所以我需要在选择中使用匿名类型的动态连接。我很肯定这可以在表达式的帮助下实现,但是我在这个主题上的知识并不完美,我无法实现这个
  • 能否详细说明您的查询。如果您能提供您的实际查询并告诉我们您想要动态化哪些部分(这是一个词吗?),将会有很大帮助。
  • @Sefe,当然,但查询很简单:table1.Join(table2, , , )。最重要的部分是dynamicSelectQuery,它必须返回一个匿名对象
  • 您能否提供一个没有占位符的具体示例并将其添加到您的问题中。最好也显示您已经尝试过的表达方式。

标签: c# entity-framework linq


【解决方案1】:

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

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

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

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

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

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

这个表达式必须根据它们的优先级分成几部分:

  • lambda 运算符=> 发起的lambda 表达式:LambdaExpression,由Expression.Lambda 创建
  • lambda 运算符左侧的参数t1t2ParameterExpression,使用Expression.Parameter 创建
  • lambda 运算符右侧的对象实例化new DataContainerNewExpression,使用Expression.New 创建
  • =发起的初始化块内的赋值:BinaryExpression,用Expression.Assign创建
  • 分配左侧的属性:MemberExpression,使用Expression.Property 创建
  • 使用.field1 取消引用的属性:MemberExpression,使用Expression.Property 创建
  • 参数访问t1ParameterExpression,使用Expression.Parameter 创建(但您正在重用为 lambda 表达式创建的参数表达式)

如您所见,与只写下表达式(或使用动态 LINQ)相比,这相当乏味。我将用子表达式t1.field1 来举例说明:

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

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

您将其重复用于您的财产访问:

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

您在创建分配时使用的这个表达式,您将与实例化表达式中的其他分配一起使用,您将在 lamdba 表达式的右侧使用它以及其他需要的表达式。你也可以把它写成一棵树(除了你要重用的参数表达式)。

编码愉快!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多