Применить селектор клавиш Linq Func <T, TResult> на уровне одного элемента

Извините, если заголовок вводит в заблуждение, не был уверен, как это описать.

Моя конечная цель состоит в том, чтобы иметь метод расширения IQueryable<T> и некоторую форму (см. ниже, например) выражения, которое позволит мне вернуть IQueryable<EntityIndex<T>> (или аналогичный), который содержит исходный T в поле Entity и массив /ienumerable, содержащий элементы, описываемые некоторой формой выражения.

Я знаю, что на самом деле это не имеет смысла, надеюсь, это будет после примера...

Это то, что у меня есть до сих пор:

class EntityIndex<T, TKey>
{
    T Entity { get; set; }
    // Doesn't have to be IEnumerable, whatever is easier
    IEnuermable<TKey> Index { get; set; }
}
static class Elsewhere
{
    [Extension()]
    public IQueryable<EntityIndex<T, TKey>> IndexBy<T, TKey>(this IQueryable<T> source, Expression<Func<T, TKey[]>> indexSelector)
    {
        return source.Select(n => new EntityIndex<T, TKey> {
            Entity = n,
            Index = new T[] { n }.Select(indexSelector)
        });
    }
}

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

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

В идеале, мне нужно, чтобы решение понималось движком L2S, что я не убежден выше, благодаря введению класса EntityIndex, но я надеюсь, что он будет его рассматривать как анонимный класс.

EDIT:

Хорошая точка Damien, более крупная картина, вероятно, намного проще описать...

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

Короче говоря, в ряде мест в коде есть поиск подстановочных строк. Если у меня есть EntityA с Property1, Property2, Property3 и т.д., Нередко можно увидеть код, например:

Рукописный, пожалуйста, извините незначительные опечатки

public string[] WildcardSearch(string prefixText, int count)
{
    string searchTerm = prefixText.Replace(wildcard, string.Empty);
    if (prefixText.StartsWith(wildcard) && prefixText.EndsWith(wildcard)) {
        return entitySet.Where(n => n.Property1.Contains(searchTerm) || n.Property2.Contains(searchTerm)).Select(n => n.Property3).ToArray();
    } else if (prefixText.StartsWith(wildcard)) {
        return entitySet.Where(n => n.Property1.EndsWith(searchTerm) || n.Property2.EndsWith(searchTerm)).Select(n => n.Property3).ToArray();
        // you get the picture, same with EndsWith, no wildcards defaults to contains...
    }
}

EDIT:

Дальнейшее уточнение - используя приведенный выше пример WildcardEarch, я надеялся, что у вас будет селектор следующим образом или похожим:

Func<EntityA, IEnumerable<string>> indexSelector = n => new string[] {
    n.Property1,
    n.Property2
};

// Alternatively, a ParamArray of keySelector might work?
Func<EntityA, string>[] keySelectors = new Func<EntityA, string>[] {
    n => n.Property1,
    n => n.Property2
};

Учитывая адекватное выражение, описывающее, какие поля в объекте искать, возвращая IQueryable<EntitySearch<T>>, как показано выше, я надеялся применить один критерий, аналогичный следующему:

Func<EntitySearch<T>, bool> criterion = n => false;
if (wildcardIsContains) {
    criterion = n => n.Values.Any(x => x.Contains(searchTerm));
} else if (wildCardIsStartsWith) {
    criterion = n => n.Values.Any(x => x.Contains(searchTerm));
    //etc
}

Учитывая расширение на самом верху, которое я не могу заставить работать, и эту логику критерия, я должен иметь возможность принимать IQueryable<T>, выбирать некоторые поля и применять соответствующий поиск подстановочных знаков в полях, наконец возвращая IQueryable<T> снова добавив фильтрацию.

Thanks¬!

Прокомментируйте, если вам нужна дополнительная информация/разъяснение...

EDIT: Справедливая одна @субамрен и спасибо за интерес. Некоторые не общие примеры могут быть полезными. Я придумаю что-нибудь и добавлю их в ближайшее время. В настоящее время некоторые пояснения основаны на вашем комментарии...

Учитывая IQueryable<Animal>, я хочу расширение, позволяющее мне выбирать поля в Animal, которые я намерен искать/индексировать. Например, Animal.Description, Animal.Species.Name и т.д. Это расширение должно возвращать что-то вроде IIndexedQueryable<Animal>. Это проблема, с которой я пытаюсь разобраться в вопросе выше. Более широкая картина, о которой я был бы очень рад, если вы захотите помочь, заключается в следующем:

Интерфейс IIndexedQueryable<T>, в свою очередь, я хотел бы расширить расширение, для которого может потребоваться термин поиска string. Расширение должно разрешать подстановочные знаки в рамках поискового запроса, расширять исходный IQueryable с помощью необходимого критерия для выполнения поиска по индексированным полям и снова возвращать IQueryable<T>.

Я понимаю, что это можно сделать за один шаг, но я надеялся сделать это таким образом, чтобы позже я мог изучить добавление третьего метода расширения, применимого к IIndexedQueryable<T>, позволяющего выполнять поиск freetext с помощью SQL Server... ^^ Есть смысл?

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

Ответ 1

Так что-то вроде:

public static IEnumerable<EntityIndex<T, Y>> IndexBy<T, Y>(this IEnumerable<T> entities, Func<T, Y> indexSelector) {
    return entities.Select(e => new EntityIndex<T, Y> { Entity = e, IndexValue = indexSelector(e) });
}

Отмечая, что в общем случае определение EntityIndex с TIndexType (называемое здесь Y) важно, потому что вы заранее не знаете, что такое индекс. Использование родовое позволяет Y быть перечислением, поэтому следующее будет работать как селектор индексов:

// Assuming Animal has attributes "Kingdom", "Phylum", "Family", "Genus", "Species"
// this returns an enumeration of EntityIndex<Animal, String[]>
var animalsClassified = someAnimals.IndexBy(a => new String[] { a.Kingdom, a.Phylum, a.Family, a.Genus, a.Species });

EDIT (добавление дополнительной информации):

Используя вышеизложенное, вы можете группировать результаты по уникальному значению индекса:

var animalClassifications = animalsClassified
                                .SelectMany(ac => ac.IndexValue.Select(iv => new { IndexValue = iv, Entity = ac.Entity }))
                                .GroupBy(kvp => kvp.IndexValue)

То, что я описал здесь, кстати, является (очень упрощенной формой) алгоритмом MapReduce, популяризированным Google. Распространенная форма того же самого обычно используется для идентификации ключевых слов в текстовом поиске, где вы хотите построить индекс (поисковый запрос) → (список содержащихся документов).