在我的应用程序中有帖子订阅者和帖子:
public class PostSubscriber {
public List<int> Topics { get; set; }
...
}
public class Post {
public int Id { get; set; }
...
}
有一个每天运行一次的预定作业,它获取最近的帖子(通常少于 30)并将它们发送给帖子订阅者。
如果订阅者有空/空
Topics
列表,那么他会收到所有最近的帖子通知。
如果订阅者有非空的 Topics
列表,那么他只会收到匹配的帖子通知。
在我当前的设置中,外循环在帖子集合上完成,订阅者在每次迭代时被过滤:
foreach(var post in posts) {
var postSubscribers = preFetchedSubscribers.Where(i =>
i.Topics == null ||
!i.Topics.Any() ||
i.Topics.Contains(post.Id)
);
...
}
但我正在考虑将电子邮件连接成一封,向订阅者发送一封信,提及所有/匹配最近的帖子,而不是每个帖子发送 N 封信。
会在外循环中迭代订阅者
foreach(var subscriber in subscribers) {
foreach(var post in posts) {
// check if post is applicable and concatenate email
}
...
// send concatenated email
}
效率更高/更低,为什么?
如果你想将电子邮件连接成一个,除了让订阅者成为外循环之外,没有什么好办法。否则,您需要为所有订阅者存储部分创建的电子邮件,直到您完成所有帖子。
这取决于帖子数、订阅者数和订阅的帖子数。如果它足够大,你应该考虑从订阅的帖子到订阅者引入字典(如果我计算正确,当前的实现是
O(p * s * t)
其中 p - 帖子数,s - 订阅者数量,t - 每个订阅者的平均主题数) .沿着这些线的东西:
var preFetchedSubscribers = new[]{new {Topics = new []{1}}};
var dictionary = preFetchedSubscribers
.SelectMany(s => s.Topics.Select(tId => (tId, s)))
.GroupBy(t => t.tId)
.ToDictionary(gr => gr.Key, gr => gr.Select(t => t.s).ToList());
foreach (var post in posts)
{
if (dictionary.TryGetValue(post.Id, out var subs))
{
// ...
}
}
或者使用
HashSet
代替List
:
public class PostSubscriber
{
public HashSet<int> Topics { get; set; } = new HashSet<int>(); // so i.Topics == null not needed
...
}
但一如既往-跑马并以具体案例为基准。