LINQ多对多交叉口

时间:2012-01-03 14:37:35

标签: c# linq entity-framework linq-to-sql

我正在尝试根据Posts

列表查询Tags
public class Post
{
  public int? Id {get;set;}
  public string Name {get;set;}
  public virtual ICollection<Tag> Tags {get;set;}
}
public class Tag
{
  public int? Id {get;set;}
  public string Name {get;set;}
  public vritual ICollection<Post> Posts {get;set;}
}

现在我想根据标签列表返回帖子: IList<Tag> searchTags = ParseTagsFromSearchString("tag1,tag2,tag3"); // this function checks the tags in the database, so all the primary keys are available in the list

当帖子包含searchTags中也存在的一个或多个标签时,它应包含在结果中。我尝试过以下方法:

var q = from s in Context.Registrations
                    where s.Tags.Intersect(tagList)
                    select s;

错误:Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<Models.Tag>' to 'bool'

var q = from s in Context.Registrations
                    where s.Tags.Any(t => tagList.Any(t2 => t.Id.Value == t2.Id.Value))
                    select s;

运行时错误:NotSupportedException: Unable to create a constant value of type 'Models.Tag'. Only primitive types ('such as Int32, String, and Guid') are supported in this context. 有什么想法吗?

- 1月4日更新: 答案指向正确的解决方案,但在我的代码中我仍然有NotSupportedException。可以为空的整数是否可能导致这种情况,因为它不是原始类型?

5 个答案:

答案 0 :(得分:3)

你几乎就在那里,只需将Intersect(taglist)更改为Intersect(taglist).Any()

以下是一个有效的示例(根据您对PostTag的定义):

Tag tag1 = new Tag() { Id = 1, Name = "tag1" };
Tag tag2 = new Tag() { Id = 2, Name = "tag2" };
Tag tag3 = new Tag() { Id = 3, Name = "tag3" };

List<Post> posts = new List<Post>() {
    new Post() { Id = 1, Name = "post1", Tags = new Tag[] {tag1} },
    new Post() { Id = 2, Name = "post2", Tags = new Tag[] {tag2} },
    new Post() { Id = 3, Name = "post3", Tags = new Tag[] {tag3} },
    new Post() { Id = 4, Name = "post13", Tags = new Tag[] {tag1, tag3} },
};

List<Tag> searchTags = new List<Tag>() { tag1, tag2 };

IEnumerable<Post> matching = posts.Where(p => p.Tags.Intersect(searchTags).Any());
//matching now contains post1, post2 and post13

现在,在实际代码中,您可能不会在搜索列表和帖子中使用相同的标签实例,因此您必须覆盖标签的Equals和GetHashCode,或者在调用Intersect时提供IEqualityComparer

答案 1 :(得分:1)

尝试将TagList转换为整数ID列表。在查询中使用它来修复NotSupportedException。

var tagIds = tagList.Select(x=>x.Id);

然后在查询中使用tagIds ...

var q = from s in Context.Registrations
                    where s.Tags.Any(t => tagIds.Any(t2 => t.Id.Value == t2.Id))
                    select s;

我不确定是否嵌套这样的任何语句都会有效。只是解释你为什么得到例外。

答案 2 :(得分:1)

今天我又遇到了这个问题,并决定解决它。我的问题是IList<Tag> searchTags = ParseTagsFromSearchString("tag1,tag2,tag3");生成一个列表,当在实体框架中的另一个查询的交叉表达式中使用时会导致异常。正确的做法是:

var q = ParseTagsFromSearchString("tag1,tag2,tag3"); // this function will now build a query instead of a list
IList<Post> posts = (from s in Context.Posts where s.Tags.Intersect(q.AsEnumerable()).Any() select s).ToList();

答案 3 :(得分:0)

也许这可行吗?

var q = from s in Context.Registrations
                    where s.Tags.Intersect(tagList).Count() != 0
                    select s;

或者

var q = from s in Context.Registrations
                    where s.Tags.Any(t => tagList.Contains(t))
                    select s;

虽然没有尝试过,所以不保证:)

答案 4 :(得分:0)

LinqPad

中将它们放在一起
void Main()
{
   var tag1 = new Tag { Name = "tag1" };
   var tag2 = new Tag { Name = "tag2" };
   var tag3 = new Tag { Name = "tag3" };

    var posts = new List<Post>
    {   
        new Post 
        {
            Name = "Post1",
            Tags = new List<Tag> { tag1, tag3 }
        },
        new Post 
        {
            Name = "Post2",
            Tags = new List<Tag> { tag2, tag3 }
        }
    };

    var tagList = new List<Tag> { tag1 };

    var q = posts.Where(x => x.Tags.Intersect(tagList).Any());

    q.Dump();
}

public class Post 
{ 
  public int? Id {get;set;} 
  public string Name {get;set;} 
  public virtual ICollection<Tag> Tags {get;set;} 
} 

public class Tag 
{ 
  public int? Id {get;set;} 
  public string Name {get;set;} 
  public virtual ICollection<Post> Posts {get;set;} 
}