ASP.NET EF - SQL - 如何获取包含多行Id

时间:2015-12-18 21:26:08

标签: sql asp.net sql-server entity-framework

我可能会或可能不会正确地做事,所以如果问题不是可回答的问题,请随意提供更好的方法,但事情可以更好地组织。

我有一个课程:资源

public class Resources
{
    public int Id....
    ...... simplified for readability.
    public String Url......
    public DateTime publishDateTime....
}

我还有一个标签列表

public class Tags
{
    public int Id....
    ...... simplified for readability.
    public String Tagname......
}

这是多对多的所以我还有更多类TagResources(简化)

 public class TagResources
{
    public int TagResourceId....
    ...... simplified for readability.
    public int TagId......
    public int ResourceId......
}

每个资源可以包含多个(最多3个)标记,但是我希望能够检索所有标记存在的所有资源。

示例表:

+-----+----------------+--------------------+
+ Id  +   TagId        +      ResourceId    + 
+-----+----------------+--------------------+
+  1  +       1        +          1         +      
+-----+----------------+--------------------+
+  2  +       2        +          1         +      
+-----+----------------+--------------------+
+  3  +       3        +          1         +      
+-----+----------------+--------------------+
+  4  +       1        +          2         +      
+-----+----------------+--------------------+
+  5  +       2        +          2         +      
+-----+----------------+--------------------+
+  6  +       4        +          2         +      
+-----+----------------+--------------------+
+  7  +       1        +          3         +      
+-----+----------------+--------------------+
+  8  +       2        +          3         +      
+-----+----------------+--------------------+
+  8  +       3        +          3         +      
+-----+----------------+--------------------+
+  8  +       4        +          3         +      
+-----+----------------+--------------------+

因此,例如,我想获取所有具有标记1,2和3的资源,它将返回ResourceId为1和3。

如果我想获得所有包含标签1,2和4的资源,它将返回ResourceId为2和3。

所以从本质上讲,我如何获得所有资源,相应的tagId位于所选列表中。

目前,我得到第一个标签匹配的所有行;

var resourceIds = _context.TagResources.Distinct()
        .Include(m => m.Tag)
        .Where(p => tags.Contains(p.Tag.Name)).Select(p => p.ResourceId); 

每个资源最多有三个唯一标记,因此我使用它来仅检索具有第二个标记的标记,然后检索具有第三个标记的标记,然后将它们添加到新列表中。

这是非常令人费解的,我在这方面不是很有经验(你可以看到)。然而,除了剩下的,我需要OrderBy publishDateTime然后Take(20)。

有人能引导我朝着正确的方向前进吗?

我正在使用带有EF的ASP.NET MVC,但任何有关SQL的指针都会有所帮助。

2 个答案:

答案 0 :(得分:1)

你应该可以使用group by来解决这个问题。我为TagResources创建了一个类,然后创建了一个与您提供的数据匹配的列表。

var resources = new List<TagResource>()
{
    new TagResource() {Id = 1, TagId = 1, ResourceId = 1},
    new TagResource() {Id = 2, TagId = 2, ResourceId = 1},
    new TagResource() {Id = 3, TagId = 3, ResourceId = 1},
    new TagResource() {Id = 4, TagId = 1, ResourceId = 2},
    new TagResource() {Id = 5, TagId = 2, ResourceId = 2},
    new TagResource() {Id = 6, TagId = 4, ResourceId = 2},
    new TagResource() {Id = 7, TagId = 1, ResourceId = 3},
    new TagResource() {Id = 8, TagId = 2, ResourceId = 3},
    new TagResource() {Id = 9, TagId = 3, ResourceId = 3},
    new TagResource() {Id = 10, TagId = 4, ResourceId = 3},
};

使用它作为我的数据源我想出了这个:

var input = new int[] { 1, 2, 3 }; // the ids to search for.  you may change these anyway you want

var results = resources.GroupBy(x => x.ResourceId, r => new { TagId = r.TagId })
    .Where(x => input.All(x.Select(r => r.TagId).Contains)); // performs the group by and filters out results that don't include all the keys

foreach (var result in results)
{
    Console.WriteLine(result.Key);
}

outputs:
//1
//3

答案 1 :(得分:1)

这并不像看起来那么简单。您无法使用此查询...

var tagIds = new int[] { 1, 2, 3 };

var resources = _context.Resources
                        .Where(r => r.TagResources
                                     .All(tr => tagIds.Contains(tr.TagId)));

...因为它只返回所有标签都在tagIds的资源,所以资源3不符合条件。

相反,您必须找到其标记包含tagIds的资源:

var resources = _context.Resources
                        .Where(r => tagIds
                        .All(id => r.TagResources
                                    .Select(tr => tr.TagId)
                                    .Contains(id)));

您会注意到这会产生不成比例的SQL,因为EF必须将tagIds转换为各种SQL表。但是,有了这个有限的Id值,这绝对没有问题。