Linq основывал универсальную альтернативу к Предикату <T>?

Мне назвали интерфейс ICatalog как показано, ниже где каждый ICatalog имеет имя и метод, который возвратит объекты на основе a Predicate<Item> функция.

public interface ICatalog
{
     string Name { get; }
     IEnumerable<Item> GetItems(Predicate<Item> predicate);
}

Определенная реализация каталога может быть связана с каталогами в различном формате, такими как XML или база данных SQL.

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

Все же с реализацией SQL я не получил бы все содержание базы данных в память и затем отфильтровал бы объекты с функцией предиката. Вместо этого я хотел бы найти способ так или иначе передать предикат SQL-серверу или так или иначе преобразовать его в SQL-запрос.

Это походит на проблему, которая может быть решена с Linq, но я довольно плохо знаком с ним. Мой интерфейс должен возвратить IQueryable вместо этого? Я не заинтересован прямо сейчас с тем, как на самом деле реализовать версию SQL моего ICatalog. Я просто хочу удостовериться, что мой интерфейс будет допускать его в будущем.

9
задан Eric Anastas 6 May 2010 в 13:35
поделиться

2 ответа

Роб указал, как это можно сделать (хотя более классический подход LINQ может использовать Expression > , и, возможно, вернуть IQueryable ).

Хорошая новость заключается в том, что если вы хотите использовать предикат с LINQ-to-Objects (для вашего xml-сценария), вы можете просто использовать:

Predicate<Item> func = predicate.Compile();

или (для другой подписи):

Func<Item,bool> func = predicate.Compile();

и у вас есть делегат ( func ) для тестирования ваших объектов.

Проблема, однако, в том, что это кошмар для модульного тестирования - вы можете действительно интегрировать проверить его.

Проблема в том, что вы не можете надежно имитировать (с помощью LINQ-to-Objects) что-либо, связанное со сложными хранилищами данных; например, следующее будет нормально работать в ваших модульных тестах, но не будет работать «по-настоящему» с базой данных:

var foo = GetItems(x => SomeMagicFunction(x.Name));

static bool SomeMagicFunction(string name) { return name.Length > 3; } // why not

Проблема в том, что только некоторые операции могут быть переведены в TSQL. Та же проблема возникает с IQueryable - например, EF и LINQ-to-SQL поддерживают разные операции с запросом; даже просто First () ведет себя по-другому (EF требует, чтобы вы явно упорядочили его первым, LINQ-to-SQL - нет).

Итак, вкратце:

  • это может работать
  • , но хорошо подумайте, хотите ли вы это сделать; более классический интерфейс репозитория / службы черного ящика может быть более тестируемым
7
ответ дан 2 November 2019 в 23:59
поделиться

Вам не нужно идти до конца и создавать реализацию IQueryable

Если вы объявите свой метод GetItems как:

IEnumerable<IFamily> GetItems(Expression<Predicate<Item>> predicate);

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

Прочитайте статью IQueryable, потому что в ней объясняется, как создать посетителя дерева выражений, который вам понадобится для создания простой версии.

5
ответ дан 2 November 2019 в 23:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: