Требуется помощь в методе расширения LINQ - PullRequest
1 голос
/ 05 августа 2010

это пока мой самый сложный вопрос, и я надеюсь, что кто-то уже сталкивался с этим вопросом и нашел элегантный ответ. По сути, у меня есть несколько методов расширения linq (которые просто бывают в дозвуковом режиме, но будут применимы в любой производной linq), которые работают отлично (расширения для .WhereIn () и .WhereNotIn ()). эти методы работают для преобразования linq в sql эквиваленты in (). Теперь приведенный ниже код отлично работает при предоставлении известных типизированных параметров (то есть массива или массива params):

public static IQueryable<T> WhereIn<T, TValue>(
    this IQueryable<T> query, 
    Expression<Func<T, TValue>> selector, 
    params TValue[] collection) where T : class
{
    if (selector == null) throw new ArgumentNullException("selector");
    if (collection == null) throw new ArgumentNullException("collection");
    ParameterExpression p = selector.Parameters.Single();

    if (!collection.Any()) return query;

    IEnumerable<Expression> equals = collection.Select(value =>
       (Expression)Expression.Equal(selector.Body,
            Expression.Constant(value, typeof(TValue))));

    Expression body = equals.Aggregate(Expression.Or);

    return query.Where(Expression.Lambda<Func<T, bool>>(body, p));
}

использование:

var args = new [] { 1, 2, 3 };
var bookings = _repository.Find(r => r.id > 0).WhereIn(x => x.BookingTypeID, args);
// OR we could just as easily plug args in as 1,2,3 as it's defined as params
var bookings2 = _repository.Find(r => r.id > 0).WhereIn(x => x.BookingTypeID, 1,2,3,90);

Впрочем, теперь для сложной части. Я хотел бы иметь возможность передавать объект IQueryable в версию перегрузки вышеупомянутого, которая принимает второй объект linq в качестве параметра, чтобы получить эквивалент select * from table1 where table1.id in(select id from table2). вот сигнатура метода, которая на самом деле компилируется нормально, но отсутствует вся важная логика:

public static IQueryable<T> WhereIn<T, TValue, T2, TValue2>(
    this IQueryable<T> query, 
    Expression<Func<T, TValue>> selector, 
    T2 entity2, 
    Expression<Func<T2, TValue2>> selector2) where T : class
{
    if (selector == null) throw new ArgumentNullException("selector");
    if (selector2 == null) throw new ArgumentNullException("selector2");
    ParameterExpression p = selector.Parameters.Single();
    ParameterExpression p2 = selector2.Parameters.Single();

    /* this is the missing section */

    /* i'd like to see the final select generated as 
     *
     * select * from T where T.selector in(select T2.selector2 from T2)
     */


    return  null; 
    // this is just to allow it to compile - proper return value pending
}

использование:

var bookings = _repository.Find(r => r.BookingID>0)
                .WhereIn(x => x.BookingTypeID, new BookingType(), y => y.BookingTypeID);

Лаю ли я здесь несуществующее (выражение) дерево :-) - или это довольно выполнимо.

всего наилучшего - вот на что надеемся.

Джим

Ответы [ 2 ]

4 голосов
/ 05 августа 2010

Почему бы вам не использовать соединение?

var query = from x in table1
            join y in table2 on x.Id equals y.Id
            select x;

Или, если для каждого x:

может быть несколько значений y
var query = from x in table1
            join z in table2.Select(y => y.Id).Distinct() on x.Id equals z
            select x;

Я ожидаю, что подобные запросы будут хорошо оптимизированы в базах данных SQL.

Или, если вы действительно хотите использовать Where:

var query = table1.Where(x => table2.Select(y => y.Id).Contains(x.Id));

Возможно, мне не хватает чего-то большего ... или может быть, что перевод вышеуказанных запросов в методы расширения - это то, что вы ищете:

0 голосов
/ 26 августа 2010

В итоге я выбрал метод расширения для достижения этой цели, но все еще не на 100% успешен.

Через некоторое время я опущу настоящий полный рабочий код, как только я интегрирую его со всеми другими опциями.

...