8

我有一个 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 Join 语法,但在这种情况下,数据不是来自 SQL 源,所以我需要在代码中进行连接...但正如我所说我有 2 个结果集,并且 Linq to Objects 可以进行连接...(以及后来的投影)只需替换表达式常量MyProv.Table<Customer>MyProv.Table<Order>使用List<Customer>andList<Order>并让List<>提供者处理表达式……这可能吗?如何?

4

5 回答 5

6

前面的两个答案都有效,但如果您使用 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
于 2009-03-30T04:40:47.973 回答
3

我所追求的事情是用具体的 IEnumerable(或通过 .AsQueryable() 的 IQueryable)结果集替换表达式树中的 Queryable<> 常量......这是一个复杂的主题,可能只对 Linq 有意义在表达树访问者等方面深入研究的提供者作家。

我在 msdn 演练中找到了一个片段,它的功能类似于我所追求的,这给了我前进的方向......

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;
        }
    }
}
于 2009-04-28T22:53:44.673 回答
2

如果您实现了存储库模式,则只需提供 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 模式进行建模,就像本文说明的那样。

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

LINQ 2 SQL 提供程序的 Repository 方法如下所示:

public IQueryable<T> Repository<T>() where T : class
{
    ITable table = _context.GetTable(typeof(T));
    return table.Cast<T>();
}
于 2010-07-01T20:18:34.143 回答
1

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

例如(想想 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。

于 2009-03-30T01:15:15.613 回答
0

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

var res = ((IEnumerable<Foo>)dc.Foos
            .Where(x => x.Bla > 0))  // IQueryable
          .Where(y => y.Snag > 0)   // IEnumerable
于 2009-03-30T03:06:37.980 回答