我有这两个实体(在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)。我在这里错过了什么?有什么好主意吗?提前谢谢。
答案 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();
}
}