Building where clauses to evaluate collections inside a many to many connection using LINQ to entities

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

Question

I thus use the Linq entity framework. I have two things.Content and Tag They relate to one another on a many-to-many basis.Content may have a lotTags and Tag may have a lotContents . In order to pick all contents when any tag names are same, I am attempting to create a query.blah

As a property, each of the entities has a collection of the other entity (but no IDs). This is where I'm having trouble. I do have a special phrase forContains You may presume that I can do a "contains" for a collection so that whomever may assist me can. I borrowed this phrase from: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2670710&SiteID=1

Edit 1

I ultimately came to my own conclusion.

1
18
5/23/2017 12:34:51 PM

Accepted Answer

After reading about the PredicateBuilder, perusing the great comments that people sent my way, commenting on other websites, and reading more about the Integrating Predicates and Map of Canonical Functions, I also took some of what I learned from the LINQ queries that call functions (some of these classes were taken from these pages).

I've found a solution AT LAST! But there is one part that seems a little hacked...

Let's finish the hacked section now:(

I had to replicate the ExpressionVisitor class that is tagged as internal using Reflector. Then, in order to make it function, I had to make a few little adjustments. Because it was throwing internal exceptions, I had to generate two exceptions. The return value of the ReadOnlyCollection() function required to be changed from:

return sequence.ToReadOnlyCollection<Expression>();

To:

return sequence.AsReadOnly();

I'd share the class, but it's a big one, and I don't want to make this article any more cluttered than it currently is. In the future, I hope Microsoft will make that class public and I may delete it from my library. Moving forward

I included the class 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);
        }
    }

Then I introduced a class called 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);
        }
    }

And PredicateBuilder was the last class I added.

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

}

This is the outcome I got. This code worked, and I was able to get the "content" entities that had "tag" entities that matched the tags I was looking for!

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

Please let me know if you want assistance with the same problem, if you would like me to email you the necessary data, or if you just require additional information.

18
9/25/2008 5:04:08 AM

Popular Answer

To summarize...

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

So, is it what you were hoping for?

I'm not really sure.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow