Выполнение части запроса IQueryable и откладывание оставшейся части до Linq для объектов - PullRequest
8 голосов
/ 30 марта 2009

У меня есть провайдер Linq, который успешно отправляет и получает данные из выбранного источника данных, но теперь я хотел бы сделать, поскольку у меня есть отфильтрованный набор результатов, - разрешить Linq to Objects обрабатывать остальную часть дерева выражений (для вещей как Joins, проекция и т. д.)

Я думал, что я мог бы просто заменить константу выражения, содержащую мой IQueryProvider, на наборы результатов IEnumerable через ExpressionVisitor, а затем вернуть это новое выражение. Также верните провайдер IEnumerable из моего IQueryable ... но это, похоже, не работает: - (

Есть идеи?

Edit: Некоторые хорошие ответы здесь, но с учетом формы ...

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> и List<Order>, и пусть провайдер List<> обработает выражение ... это возможно? как?

Ответы [ 5 ]

6 голосов
/ 30 марта 2009

Оба предыдущих ответа работают, но читается лучше, если вы используете 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 });

EDIT:

// ... 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
3 голосов
/ 29 апреля 2009

После этого я заменил константу Queryable <> в дереве выражений конкретным набором результатов IEnumerable (или IQueryable через .AsQueryable ()) ... это сложная тема, которая, вероятно, только смысл для авторов Linq Provider, которые по колено в посетителях дерева выражений и т. д.

Я нашел фрагмент в пошаговом руководстве 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;
        }
    }
}
2 голосов
/ 02 июля 2010

Если бы вы внедрили шаблон репозитория, вам не помешало бы просто предоставить обратно 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
          }

, а затем просто создайте своего провайдера для моделирования шаблона IQueryable в вашем методе репозитория, как эта статья иллюстрирует.

Таким образом, вы можете написать всевозможных провайдеров, чтобы использовать их для любых нужд. Вы можете иметь поставщика SQL LINQ 2 или написать поставщика памяти для своих модульных тестов.

Метод репозитория для поставщика SQL LINQ 2 будет выглядеть примерно так:

public IQueryable<T> Repository<T>() where T : class
{
    ITable table = _context.GetTable(typeof(T));
    return table.Cast<T>();
}
1 голос
/ 30 марта 2009

Если я не понимаю, я обычно просто добавляю .ToArray () в цепочку методов linq в точке, где я хочу, чтобы выполнялся поставщик 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.

0 голосов
/ 30 марта 2009

Роб отвечает хорошо, но требует полного подсчета. Вы могли бы привести синтаксис метода расширения и ленивую оценку:

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