LINQ to entity - Création de clauses où tester les collections dans une relation plusieurs à plusieurs

c# entity-framework lambda linq many-to-many

Question

Donc, j'utilise le framework d'entité Linq. J'ai 2 entités: Content et Tag . Ils sont dans une relation plusieurs à plusieurs. Content peut avoir plusieurs Tags et les Tag peuvent avoir plusieurs Contents . J'essaie donc d'écrire une requête pour sélectionner tout le contenu où les noms de balises sont égaux à blah

Les deux entités ont une collection de l'autre entité en tant que propriété (mais pas d'identifiant). C'est là que je me bats. J'ai une expression personnalisée pour Contains (donc, si quelqu'un peut m'aider, vous pouvez supposer que je peux créer un "contient" pour une collection). J'ai obtenu cette expression sur: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1

Modifier 1

J'ai fini par trouver ma propre réponse.

Réponse acceptée

Après avoir lu le PredicateBuilder , lire tous les messages merveilleux que les gens me ont envoyé, l' affichage sur d' autres sites, puis lire plus sur La combinaison de prédicats et de cartographie des fonctions de Canonical .. oh et je pris un peu de L' appel de fonctions dans les requêtes LINQ ( certaines de ces classes ont été tirées de ces pages).

J'ai enfin une solution !!! Bien qu'il y ait un morceau qui est un peu piraté ...

Passons au morceau piraté avec :(

Je devais utiliser un réflecteur et copier la classe ExpressionVisitor qui est marquée comme interne. J'ai ensuite dû apporter quelques modifications mineures à son fonctionnement. J'ai dû créer deux exceptions (car il s'agissait de nouvelles exceptions internes. J'ai également dû modifier le retour de la méthode ReadOnlyCollection () à partir de:

return sequence.ToReadOnlyCollection<Expression>();

À:

return sequence.AsReadOnly();

Je posterais le cours, mais il est assez volumineux et je ne veux pas encombrer ce post plus qu’il ne le sera déjà. J'espère qu'à l'avenir cette classe pourra être supprimée de ma bibliothèque et que Microsoft la rendra publique. Passer à autre chose ...

J'ai ajouté une classe 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);
        }
    }

Puis j'ai ajouté une classe 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);
        }
    }

Et la dernière classe que j'ai ajoutée était 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; }

}

Ceci est mon résultat ... J'ai pu exécuter ce code et récupérer les entités "contenu" résultantes qui ont des entités "balises" correspondantes des balises que je cherchais!

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

Faites-le moi savoir si quelqu'un a besoin d'aide avec cette même chose, si vous voulez que je vous envoie des fichiers pour cela, ou si vous avez simplement besoin de plus d'informations.


Réponse populaire

En résumé ...

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

Alors est-ce ce que vous attendez?

Je suis un peu confus.



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow