Применить Linq Funcключевой селектор на уровне одного элемента - PullRequest
10 голосов
/ 14 декабря 2011

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

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

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

Это то, что я имею до сих пор:

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:

Хороший вопрос, Дэмиен, большую картину, вероятно, гораздо проще описать ...

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

Короче говоря, во многих местах кода мы используем поиск по шаблону. Если у меня есть 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: Честная @subkamren и спасибо за интерес. Некоторые неуниверсальные примеры могут быть полезны. Я напишу что-нибудь и скоро добавлю. Пока что некоторые пояснения основаны на вашем комментарии ...

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

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

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

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

1 Ответ

4 голосов
/ 06 января 2012

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

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 });

РЕДАКТИРОВАТЬ (Добавление дополнительной детализации):

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

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

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

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