【问题标题】:Performing part of a IQueryable query and deferring the rest to Linq for Objects执行 IQueryable 查询的一部分并将其余部分推迟到 Linq for Objects
【发布时间】:2010-10-16 06:59:26
【问题描述】:

我有一个 Linq 提供程序,它成功地从我选择的数据源中获取数据,但是现在我有了过滤的结果集,我想做的是允许 Linq to Objects 处理表达式树的其余部分(用于比如连接、投影等)

我的想法是,我可以通过 ExpressionVisitor 将包含 IQueryProvider 的表达式常量替换为结果集 IEnumerable,然后返回该新表达式。还从我的 IQueryable 中返回 IEnumerable 的提供程序...但这似乎不起作用:-(

有什么想法吗?

编辑: 这里有一些很好的答案,但给出的形式......

var qry = from c in MyProv.Table<Customer>()
          Join o in MyProv.Table<Order>() on c.OrderID equals o.ID
          select new 
          {
            CustID = c.ID,
            OrderID = o.ID
          }

在我的提供程序中,我可以轻松地从客户和订单中取回 2 个结果集,如果数据来自 SQL 源,我只需构造并传递 SQL 连接语法,但在这种情况下,数据不是来自 SQL源,所以我需要在代码中进行连接......但正如我所说,我有 2 个结果集,并且 Linq to Objects 可以进行连接......(以及后来的投影)只需替换表达式常量MyProv.Table&lt;Customer&gt;MyProv.Table&lt;Order&gt;List&lt;Customer&gt;List&lt;Order&gt; 并让List&lt;&gt; 提供程序处理表达式...这可能吗?怎么样?

【问题讨论】:

    标签: c# linq expression-trees iqueryable


    【解决方案1】:

    前面的两个答案都有效,但如果您使用 AsEnumerable() 将 IQueryable 转换为 IEnumerable,则效果会更好:

    // Using Bob's code...
    var result = datacontext.Table
       .Where(x => x.Prop == val)
       .OrderBy(x => x.Prop2)
       .AsEnumerable()  //  <---- anything after this is done by LINQ to Objects
       .Select(x => new { CoolProperty = x.Prop, OtherProperty = x.Prop2 });
    

    编辑:

    // ... or MichaelGG's
    var res = dc.Foos
               .Where(x => x.Bla > 0)  // uses IQueryable provider
               .AsEnumerable()
               .Where(y => y.Snag > 0); // IEnumerable, uses LINQ to Objects
    

    【讨论】:

      【解决方案2】:

      我所追求的事情是用具体的 IEnumerable(或通过 .AsQueryable() 的 IQueryable)结果集替换表达式树中的 Queryable 常量......这是一个复杂的主题,可能只会使任何对在表达式树访问者等方面有深入了解的 Linq Provider 编写者有感觉。

      我在 msdn 演练中发现了一个 sn-p,它的功能类似于我所追求的,这为我提供了前进的道路...

      using System;
      using System.Linq;
      using System.Linq.Expressions;
      
      namespace LinqToTerraServerProvider
      {
          internal class ExpressionTreeModifier : ExpressionVisitor
          {
              private IQueryable<Place> queryablePlaces;
      
              internal ExpressionTreeModifier(IQueryable<Place> places)
              {
                  this.queryablePlaces = places;
              }
      
              internal Expression CopyAndModify(Expression expression)
              {
                  return this.Visit(expression);
              }
      
              protected override Expression VisitConstant(ConstantExpression c)
              {
                  // Replace the constant QueryableTerraServerData arg with the queryable Place collection.
                  if (c.Type == typeof(QueryableTerraServerData<Place>))
                      return Expression.Constant(this.queryablePlaces);
                  else
                      return c;
              }
          }
      }
      

      【讨论】:

      • 我从我的膝盖感谢你 - 在表达树访问者的深处。 :) 这正是我所需要的
      【解决方案3】:

      如果您实现了存储库模式,则只需提供 IQueryable 并抽象出表即可。

      例子:

      var qry = from c in MyProv.Repository<Customer>()
                Join o in MyProv.Repository<Order>() on c.OrderID equals o.ID
                select new 
                {
                  CustID = c.ID,
                  OrderID = o.ID
                }
      

      然后只需构建您的提供程序以在您的 Repository 方法中为 IQueryable 模式建模,就像 this article 说明的那样。

      这样,您可以编写各种提供程序以用于您需要的任何东西。您可以拥有一个 LINQ 2 SQL 提供程序,或者为您的单元测试编写一个内存提供程序。

      LINQ 2 SQL 提供程序的存储库方法如下所示:

      public IQueryable<T> Repository<T>() where T : class
      {
          ITable table = _context.GetTable(typeof(T));
          return table.Cast<T>();
      }
      

      【讨论】:

      • 很有趣,这个周末我会玩这个。
      【解决方案4】:

      除非我有误解,否则我通常只是在 linq 方法链中我希望 linq 提供程序执行的位置添加 .ToArray()。

      例如(想想 Linq to SQL)

      var result = datacontext.Table
         .Where(x => x.Prop == val)
         .OrderBy(x => x.Prop2)
         .ToArray()
         .Select(x => new {CoolProperty = x.Prop, OtherProperty = x.Prop2});
      

      所以通过 OrderBy() 被翻译成 SQL,但是 Select() 是 LINQ to Objects。

      【讨论】:

        【解决方案5】:

        Rob 的回答很好,但会强制进行全面枚举。您可以强制转换以保持扩展方法语法和惰性评估:

        var res = ((IEnumerable<Foo>)dc.Foos
                    .Where(x => x.Bla > 0))  // IQueryable
                  .Where(y => y.Snag > 0)   // IEnumerable
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-01-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-12-10
          相关资源
          最近更新 更多