Есть ли способ создать запрос LINQ как переменную без источника данных (пока)? - PullRequest
5 голосов
/ 30 декабря 2011

Преамбула:
Мой основной вопрос очень похож на этот: Как я могу написать чистый репозиторий, не раскрывая IQueryable для остальной части моего приложения? , который остался без ответа. Я надеюсь, что если я подойду к проблеме по-другому и задам немного другой вопрос, я могу получить результат. Я повторю часть содержимого этого вопроса, чтобы читатели не читали его для контекста.

Проблема:
Я работаю с POCO-сущностями и Entity Framework 4. Я пытаюсь разрешить сложную специальную фильтрацию наборов сущностей на прикладном уровне, одновременно пытаясь избежать выставления IQueryable<T> за границей моего хранилища. Это оставляет меня с некоторыми осложнениями.

  • Я не хочу создавать в хранилище один метод массивного фильтра, который принимает огромный список параметров, таких как:

    IEnumerable GetFilteredCustomers(string nameFilter, string addressFilter, bool isActive, int customerId, ...)
    

    Мало того, что это чрезвычайно громоздко в использовании, но смотреть на него очень некрасиво, особенно если это в основном куча нулей и т. Д. Он также не так удобен в обслуживании, как хотелось бы.

  • Я не хочу создавать огромный набор методов фильтрации в хранилище, таких как:

    IEnumerable GetActiveCustomers()
    IEnumerable GetCustomersByName()
    

    Существует ряд проблем с этим подходом, включая необходимость в огромном списке методов, который увеличивается до n!, где n - количество доступных условий фильтрации, если я хочу иметь возможность комбинировать их произвольным образом. (то есть всех активных клиентов с именем Джордж). Также очень трудно поддерживать.

  • Я не хочу создавать цепочечные методы (Fluent Interface), которые манипулируют IEnumerable<T>, потому что в конечном счете это включает в себя возврат огромного набора результатов из базы данных и его фильтрацию в памяти, которая не является масштабируемым решением. .

  • Я не могу создать интерфейс Fluent, который манипулирует IQueryable<T>, потому что, как я уже сказал, я не хочу показывать IQueryable<T> мимо репозиториев.

  • Я бы хотел избежать простой перефразировки метода одиночного массивного фильтра, передавая объект, полный параметров, вместо большого списка параметров, хотя на данный момент это может быть наименее уродливым решением.

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

уточнить; в отличие от простого создания объекта параметров, как упомянуто выше, я хотел бы использовать необработанные запросы LINQ, но каким-то образом сохранить их в переменной и позже применить их к источнику данных. Я подозреваю, что тип возвращаемого значения должен быть известен заранее, но я прекрасно справлюсь с определением этого и заранее знаю его.

Чтобы рассмотреть это с другой стороны, рассмотрим следующее:

IQueryable<Customer> filteredCustomers = customerRepository.GetAll()
    .Where(c => c.FirstName == "Dave")
    .Where(c => c.IsActive == true)
    .Where(c => c.HasAddress == true)
    ;

Я хочу упаковать три предложения Where в качестве объекта запроса, полностью отделенного от customerRepository.GetAll (), передать его в качестве параметра и применить его позже.

Ответы [ 2 ]

10 голосов
/ 30 декабря 2011

Конечно.Вы можете написать метод, например:

public Expression<Func<Customer, bool>> GetDave()
{
    return c => c.FirstName == "Dave"
             && c.IsActive
             && c.HasAddress;
}

... и методы репозитория, такие как:

public IEnumerable<Customer> GetOneGuy(Expression<Func<Customer, bool>> criteria)
{
    return Context.Customers.Where(criteria);
}

... и вызвать:

var dave = Repository.GetOneGuy(this.GetDave()).Single();
1 голос
/ 31 декабря 2011

Если вы просто хотите перехватывать запросы такого рода повторно, вы можете реализовать это как методы расширения в IQueryable<Customer> (или в любом другом POCO).Что-то вроде:

public static class ExtnensionsForIQueryableCustomer
{
    public static IEnumerable<Customer> WhereActiveWithAddressAndNamed (this IQueryable<Customer> queryable, string name)
    {
        return queryable.Where (c => c.FirstName == name)
                        .Where (c => c.IsActive)
                        .Where (c => c.HasAddress);
    }
}

Вы можете использовать это как:

customerRepository.GetAll ().WhereActiveWithAddressAndNamed ("Dave");
...