LINQ to entities – 构建where子句以在多对多关系中测试集合

所以,我正在使用Linqentity framework。 我有2个实体: ContentTag 。 他们彼此处于多对多的关系中。 Content可以有很多TagsTag可以有很多Contents 。 所以我试着写一个查询来选择任何标签名称等于blah所有内容

这些实体都将另一个实体的集合作为属性(但没有ID)。 这是我在努力的地方。 我确实有Contains的自定义表达式(所以,无论谁可以帮助我,你可以假设我可以为一个集合做一个“包含”)。 我得到了这个表达式: http : //forums.microsoft.com/MSDN/ShowPost.aspx?PostID = 2670710&SiteID = 1

编辑1

我最终找到了自己的答案。

阅读了有关PredicateBuilder的内容 ,阅读了人们发给我的所有精彩post,在其他网站上发布,然后阅读更多关于Combining Predicates和Canonical Function Mapping的内容 ..哦,我从LINQ查询中的Calling函数中找到了一些内容(其中一些类来自这些页面)。

我终于有了解决方案! 虽然有一块有点被黑了……

让我们通过以下方式获取被攻击的一块:(

我不得不使用reflection器并复制标记为内部的ExpressionVisitor类。 然后我不得不对它进行一些小的改动,让它发挥作用。 我不得不创建两个exception(因为它是新的内部exception。我还必须更改ReadOnlyCollection()方法的返回:

 return sequence.ToReadOnlyCollection(); 

至:

 return sequence.AsReadOnly(); 

我会发布课程,但它很大,我不想把这个post弄得比它已经存在的更混乱。 我希望将来可以从我的库中删除该类,并且Microsoft将公开它。 继续…

我添加了一个ParameterRebinder类:

 public class ParameterRebinder : ExpressionVisitor { private readonly Dictionary map; public ParameterRebinder(Dictionary map) { this.map = map ?? new Dictionary(); } public static Expression ReplaceParameters(Dictionary 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 Compose(this Expression first, Expression second, Func 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 => ps, p => pf); // 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(merge(first.Body, secondBody), first.Parameters); } public static Expression> And(this Expression> first, Expression> second) { return first.Compose(second, Expression.And); } public static Expression> Or(this Expression> first, Expression> second) { return first.Compose(second, Expression.Or); } } 

我添加的最后一节课是PredicateBuilder:

 public static class PredicateBuilder { public static Expression> True() { return f => true; } public static Expression> False() { return f => false; } } 

这是我的结果……我能够执行此代码并从我正在搜索的标签中获取具有匹配“标记”实体的结果“内容”实体!

  public static IList GetAllContentByTags(IList tags) { IQueryable contentQuery = ... Expression> predicate = PredicateBuilder.False(); foreach (Tag individualTag in tags) { Tag tagParameter = individualTag; predicate = predicate.Or(p => p.Tags.Any(tag => tag.Name.Equals(tagParameter.Name))); } IQueryable resultExpressions = contentQuery.Where(predicate); return resultExpressions.ToList(); } 

如果有人需要这方面的帮助,请告诉我,如果您希望我为此发送文件,或者只是需要更多信息。

总结一下……

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

那是你所期待的那样吗?

我有点困惑。

这就是问题本身所要求的:

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

我不确定究竟是什么思考过程才能找到提问者的代码,而且我并不完全确定它到底在做什么。 我真正确定的一件事是.AsQueryable()调用是完全没必要的 – 或者.Tags已经是IQueryable,或者.AsQueryable()只是为你假装 – 在哪里添加额外的调用没有必要。

该错误与’tags’变量有关。 LINQ to Entities不支持作为值集合的参数。 简单地调用tags.AsQueryable() – 正如一个更早的答案所建议的那样 – 也不会起作用,因为默认的内存LINQ查询提供程序与LINQ to Entities(或其他关系提供程序)不兼容。

作为解决方法,您可以使用表达式API手动构建filter(请参阅此论坛post )并按如下方式应用它:

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

tags变量从哪里初始化? 它是什么?

注意:请编辑问题本身,而不是回答答案 – 这不是讨论主题,他们可以随时重新排序

如果您要搜索标记有任何一组标记的所有内容:

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

看起来您可能正在使用LINQ To SQL,在这种情况下,如果您编写存储过程来执行此操作可能会更好:使用LINQ执行此操作可能无法在SQL Server上运行 – 它很可能会尝试从contentQuery下拉所有contentQuery并获取所有.Tags集合。 不过,我必须设置一个服务器来检查它。