如何在n-n关系中获得最频繁重复的对象?

时间:2016-10-06 09:41:44

标签: c# asp.net-mvc entity-framework c#-4.0 entity-framework-6

我有这两个实体(在C#和EntityFramework中):

public class Article 
{ 
    public int Id { get; set; }
    public IList<Tag> Tags { get; set; }
}

public class Tag 
{
    public int Id { get; set; }
    public IList<Article> Articles { get; set; }
}

具有n-n关系。我正在加载我的文章,就像你在这个片段中看到的一样简单:

var model = _context.Articles
                    .AsNoTracking()
                    .Include(a => a.Tags)
                    .ToList();

现在,我正试图通过此片段获取文章中重复次数最多的Tag

var tags = model.SelectMany(a => a.Tags)
                .GroupBy(t => new { t.Name, t.Articles })
                .OrderByDescending(g => g.Count())
                .Take(10);

但它似乎不起作用(我为所有标签得到Count = 1)。我在这里错过了什么?有什么好主意吗?提前谢谢。

3 个答案:

答案 0 :(得分:2)

回答你的具体问题:

  

但它似乎不起作用(我为所有标签获得Count = 1)。我在这里缺少什么?

这是因为您已将tag.Articles包含在分组键中。通常,如果具有相同Tag的{​​{1}}个对象共享同一个实例,则不会出现问题(冗余和不必要的比较除外)。但是,由于您使用无跟踪查询(Id)填充了model,因此表示同一记录的.AsNoTracking()对象实际上是不同的实例(数据重复),因此它们是{ {1}}列表,因此group by无法按预期工作。通过以下代码段可以很容易地看到它:

Tag

要解决此问题,请从分组键中删除Articles或排除foreach (var tagGroup in model.SelectMany(a => a.Tags).GroupBy(t => t.Id)) { Tag prevTag = null; foreach (var tag in tagGroup) { if (prevTag != null) Debug.Assert(prevTag == tag); // FAIL! prevTag = tag; } }

AsNotracking

答案 1 :(得分:1)

如果您感兴趣的是Article s Tag s,那么为什么不选择相反的方式:

var model = _context.Tags.AsNoTracking()
                    .Include(t => t.Articles)
                    .OrderByDescending(t => t.Articles.Count())
                    .Take(10)
                    .ToList();

答案 2 :(得分:0)

作为Gilad Green的答案的一小部分,这里有一个小程序填充样本集,然后选择前100个标签,这些标签最多出现在文章中

class Program
{
    public class Article
    {
        public Article()
        {
            Tags = new HashSet<Tag>();
        }

        public int Id { get; set; }
        public HashSet<Tag> Tags { get; set; }
    }

    public class Tag
    {
        public Tag()
        {
            Articles = new HashSet<Article>();
        }

        public int Id { get; set; }
        public HashSet<Article> Articles { get; set; }
    }

    static void Main(string[] args)
    {

        List<Article> articles = new List<Article>();
        List<Tag> tags = new List<Tag>();

        int n = 100;

        for (int i = 0; i < n; i++)
        {
            tags.Add(new Tag() { Id = i });
        }

        int v = 100;

        Random rnd = new Random();

        for (int i = 0; i < v; i++)
        {
            var article = new Article() { Id = i };

            int c = rnd.Next(1,v);

            for (int k = 0; k < c; k++)
            {
                var tag = tags[rnd.Next(0, v - 1)];

                article.Tags.Add(tag);
                tag.Articles.Add(article);
            }

            articles.Add(article);
        }


        var orderedbyarticles = tags.OrderBy(x => x.Articles.Count).Take(100).ToList();

        foreach(var item in orderedbyarticles)
        {
            System.Console.WriteLine(item.Id);    
        }

        System.Console.ReadLine();
    }
}