LINQ to entity - создание предложений where для тестирования коллекций в рамках отношений "многие ко многим"

Итак, я использую структуру сущностей Linq. У меня 2 объекта: Content и Tag. Они находятся в отношениях «многие-ко-многим» друг с другом. Content может иметь много Tags, а Tag может иметь много Contents. Итак, я пытаюсь написать запрос для выбора всего содержимого, в котором любые имена тегов равны blah.

У обеих сущностей есть коллекция другой сущности в качестве свойства (но без идентификаторов). Вот где я борюсь. У меня есть собственное выражение для Contains (так что, кто бы мне ни помог, вы можете предположить, что я могу сделать "содержит" для коллекции). Я получил это выражение от: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1

Редактировать 1

В итоге я нашел свой собственный ответ.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
18
0
20 335
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

tags.Select(testTag => testTag.Name)

Откуда инициализируется переменная тегов? Что это такое?

Ой, извините .. Это передано как простой IList <Tag>.

Phobis 21.09.2008 19:01

Это то, о чем спрашивает сам вопрос:

contentQuery.Where(
    content => content.Tags.Any(tag => tag.Name == "blah")
);

Я не уверен, каков был мыслительный процесс, чтобы добраться до кода вопрошающего, на самом деле, и я не совсем уверен, что именно он делает на самом деле. Единственное, в чем я действительно уверен, так это то, что вызов .AsQueryable () совершенно не нужен - либо .Tags уже является IQueryable, либо .AsQueryable () просто подделает его для вас - добавив дополнительные вызовы туда, где там не должно быть ничего.

Подводя итог ...

contentQuery.Where(
    content => content.Tags.Any(tag => tags.Any(t => t.Name == tag.Name))
);

Так это то, чего вы ожидаете?

Я немного запутался.

Да ... это примерно то, что мне нужно ... Теперь я получаю сообщение об ошибке:> Невозможно создать постоянное значение типа «Тип закрытия». В этом контексте поддерживаются только примитивные типы (такие как Int32, String и Guid). Он исходит от: .Any (t => t.Name == tag.Name)

Phobis 22.09.2008 21:39

ПРИМЕЧАНИЕ: пожалуйста, отредактируйте сам вопрос, а не отвечайте на него - это не тема обсуждения, и они могут изменить порядок в любое время.

Если вы ищете все Содержимое, отмеченное одним из набора тегов:

IEnumerable<Tag> otherTags;
...
var query = from content in contentQuery
            where content.Tags.Intersection(otherTags).Any()
            select content;

Похоже, вы можете использовать LINQ To SQL, и в этом случае было бы лучше, если бы вы написали хранимую процедуру для этого: использование LINQ для этого, вероятно, не будет работать на SQL Server - очень вероятно, что он попытается снимите все с contentQuery и получите все коллекции .Tags. Однако мне пришлось бы настроить сервер, чтобы проверить это.

Я не использую LINQ to SQL, это LINQ to Entities.

Phobis 22.09.2008 21:42

Если я это сделаю, я получаю сообщение об ошибке: Невозможно создать постоянное значение типа «Тип закрытия». В этом контексте поддерживаются только примитивные типы (такие как Int32, String и Guid).

Phobis 22.09.2008 21:43

Ошибка связана с переменной «теги». LINQ to Entities не поддерживает параметр, представляющий собой набор значений. Простой вызов tags.AsQueryable () - как предлагается в более раннем ответе - не будет работать либо потому, что поставщик запросов LINQ в памяти по умолчанию несовместим с LINQ to Entities (или другими реляционными поставщиками).

В качестве обходного пути вы можете вручную создать фильтр с помощью API выражений (см. это сообщение на форуме) и применить его следующим образом:

var filter = BuildContainsExpression<Element, string>(e => e.Name, tags.Select(t => t.Name));
var query = source.Where(e => e.NestedValues.Any(filter));

Это вроде помогло ... но я все еще не получил от этого рабочего решения :(

Phobis 25.09.2008 08:32
Ответ принят как подходящий

Прочитав о PredicateBuilder, прочитав все замечательные сообщения, которые мне присылали люди, разместили на других сайтах, а затем прочитали больше о Комбинирование предикатов и Каноническое отображение функций ... ох, и я немного почерпнул из Вызов функций в запросах LINQ (некоторые из этих классов были взяты из эти страницы).

НАКОНЕЦ У меня есть решение !!! Хотя есть кусок, который немного взломан ...

Давайте закончим взломанный кусок с помощью :(

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

return sequence.ToReadOnlyCollection<Expression>();

К:

return sequence.AsReadOnly();

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

Я добавил класс ParameterRebinder:

public class ParameterRebinder : ExpressionVisitor {
        private readonly Dictionary<ParameterExpression, ParameterExpression> map;

        public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map) {
            this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
        }

        public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) {
            return new ParameterRebinder(map).Visit(exp);
        }

        internal override Expression VisitParameter(ParameterExpression p) {
            ParameterExpression replacement;
            if (map.TryGetValue(p, out replacement)) {
                p = replacement;
            }
            return base.VisitParameter(p);
        }
    }

Затем я добавил класс ExpressionExtensions:

public static class ExpressionExtensions {
        public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) {
            // build parameter map (from parameters of second to parameters of first)
            var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);

            // replace parameters in the second lambda expression with parameters from the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

            // apply composition of lambda expression bodies to parameters from the first expression 
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) {
            return first.Compose(second, Expression.And);
        }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) {
            return first.Compose(second, Expression.Or);
        }
    }

И последний добавленный мной класс был PredicateBuilder:

public static class PredicateBuilder {
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

}

Это мой результат ... Я смог выполнить этот код и получить обратно результирующие сущности «содержимого», которые имеют соответствующие сущности «тегов» из тегов, которые я искал!

    public static IList<Content> GetAllContentByTags(IList<Tag> tags) {
        IQueryable<Content> contentQuery = ...

        Expression<Func<Content, bool>> predicate = PredicateBuilder.False<Content>();

        foreach (Tag individualTag in tags) {
            Tag tagParameter = individualTag;
            predicate = predicate.Or(p => p.Tags.Any(tag => tag.Name.Equals(tagParameter.Name)));
        }

        IQueryable<Content> resultExpressions = contentQuery.Where(predicate);

        return resultExpressions.ToList();
    }

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

Вы можете использовать pastebin.com или gist.github.com, чтобы вставить свою взломанную версию ExpressionVisitor, этот класс может быть кому-то полезен :)

chakrit 28.12.2009 16:51

могу я получить копию кода. Вы можете написать мне на [email protected]. Я не смог найти твою электронную почту.

mikemurf22 14.03.2011 20:33

Другие вопросы по теме