Выполнение части запроса IQueryable и передача остальной части Linq для объектов
у меня есть поставщик Linq, который успешно идет и получает данные из выбранного источника данных, но то, что я хотел бы сделать теперь, когда у меня есть мой отфильтрованный набор результатов, - это разрешить Linq объектам обрабатывать остальную часть дерева выражений (для таких вещей, как соединения, проекция и т. д.)
Я думал, что могу просто заменить константу выражения, содержащую мой IQueryProvider, результирующими наборами IEnumerable через ExpressionVisitor, а затем вернуть это новое выражение. Также Верните Поставщик IEnumerable из моего IQueryable...но это, кажется, не работает : - (
есть идеи?
изменить: Некоторые хорошие ответы здесь, но с учетом формы...
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 для объектов могут выполнять соединение...(и позже проекция) было бы очень хорошо просто заменить константы выражения MyProv.Table<Customer>
и MyProv.Table<Order>
С List<Customer>
и List<Order>
и пусть а List<>
провайдер обрабатывает выражение...это возможно? как?
5 ответов
оба предыдущих ответа работают, но он читается лучше, если вы используете 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
то, что я искал, заменяло константу Queryable в дереве выражений конкретным IEnumerable (или IQueryable via.AsQueryable()) результирующего набора...это сложная тема, которая, вероятно,имеет смысл только для авторов поставщиков 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;
}
}
}
Если вы реализовали шаблон репозитория, вам может сойти с рук, просто предоставив IQueryable back и абстрактную таблицу.
пример:
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 или поставщик in memory для модульные тесты.
метод репозитория для поставщика SQL LINQ 2 будет выглядеть примерно так:
public IQueryable<T> Repository<T>() where T : class
{
ITable table = _context.GetTable(typeof(T));
return table.Cast<T>();
}
Если я не ошибаюсь, Я обычно просто добавляю .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.
ответ Роба хорош,но заставляет полное перечисление. Вы можете бросить, чтобы сохранить синтаксис метода расширения и ленивую оценку:
var res = ((IEnumerable<Foo>)dc.Foos
.Where(x => x.Bla > 0)) // IQueryable
.Where(y => y.Snag > 0) // IEnumerable