在C#中优化算法和/或结构

bom*_*sen 9 c# algorithm umbraco

我正在开发一个应用程序,您可以订阅新闻稿并选择要订阅的类别.有两组不同的类别:城市和类别.

在发送电子邮件(预定的品尝)时,我必须在发送电子邮件之前查看订阅者订阅的城市和类别.也就是说,如果我已经订阅了"伦敦"和"曼彻斯特"为我选择的城市,选择了"食品","布"和"电子"作为我的类别,我只会得到,涉及到这些时事通讯.

结构如下:

在一把umbraco CMS每newsitem有城市和类别的逗号分隔字符串(实际上,这些被存储为节点ID,因为城市和类别是一把umbraco藏汉节点)当我预订一个或多个城市,一个或多个类别,我店自定义表中数据库中的city和category nodeids.我的关系映射看起来像这样:

在此输入图像描述

整个结构看起来像这样:

在此输入图像描述

对我来说,这似乎是两组1 - 1 ..*关系(一个或多个城市的一个订户和一个或多个类别的一个订户)

要查找要向哪个订阅者发送哪些电子邮件,我的代码如下所示:

private bool shouldBeAdded = false;

// Dictionary containing the subscribers e-mail address and a list of news nodes which should be sent
Dictionary<string, List<Node>> result = new Dictionary<string, List<Node>>();

foreach(var subscriber in subscribers)
{
    // List of Umbraco CMS nodes to store which nodes the html should come from
    List<Node> nodesToSend = new List<Node> nodesToSend();

    // Loop through the news
    foreach(var newsItem in news)
    {
        // The news item has a comma-separated string of related cities
        foreach (string cityNodeId in newsItem.GetProperty("cities").Value.Split(','))
        {
            // Check if the subscriber has subscribed to the city
            if(subscriber.CityNodeIds.Contains(Convert.ToInt32(cityNodeId)))
            {
                 shouldBeAdded = true;
            }
        }

        // The news item has a comma-separated string of related categories
        foreach (string categoryNodeId in newsItem.GetProperty("categories").Value.Split(','))
        {
            // Check if the subscriber has subscribed to the category
            if(subscriber.CategoryNodeIds.Contains(Convert.ToInt32(categoryNodeId)))
            {
                shouldBeAdded = true;
            }
        }
    }

    // Store in list
    if (shouldBeAdded)
    {
        nodesToSend.Add(newsItem);
    }

    // Add it to the dictionary
    if (nodesToSend.Count > 0)
    {
        result.Add(subscriber.Email, nodesToSend);
    }
}

// Ensure that we process the request only if there are any subscribers to send mails to
if (result.Count > 0)
{
    foreach (var res in result)
    {
        // Finally, create/merge the markup for the newsletter and send it as an email.
    } 
}
Run Code Online (Sandbox Code Playgroud)

虽然这很有效,但是当我们进入三个嵌套的foreach循环时,我会在达到一定数量的订阅者时对性能有点担心.此外,记住我的老教师讲道:"每个for循环都有一个更好的结构"

所以,我希望你对上述解决方案有所了解,有没有什么可以用给定的结构改进?随着时间的推移会导致性能问题吗?

非常感谢任何帮助/提示!:-)

提前致谢.

所以经过几个小时的调试和fumblin'后,我终于想出了一些有用的东西(最初,看起来我的原始代码工作,但它没有)

可悲的是,我无法使用我尝试的任何LINQ查询,所以我回到了"ol'学校'的迭代方式;-)最终算法看起来像这样:

private bool shouldBeAdded = false;

// Dictionary containing the subscribers e-mail address and a list of news nodes which should be sent
Dictionary<string, List<Node>> result = new Dictionary<string, List<Node>>();

foreach(var subscriber in subscribers)
{
    // List of Umbraco CMS nodes to store which nodes the html should come from
    List<Node> nodesToSend = new List<Node> nodesToSend();

    // Loop through the news
    foreach(var newsItem in news)
    {
         foreach (string cityNodeId in newsItem.GetProperty("cities").Value.Split(','))
         {
             // Check if the subscriber has subscribed to the city
             if (subscriber.CityNodeIds.Contains(Convert.ToInt32(cityNodeId)))
             {
                 // If a city matches, we have a base case
                 nodesToSend.Add(newsItem);
             }
         }

         foreach (string categoryNodeId in newsItem.GetProperty("categories").Value.Split(','))
         {
             // Check if the subscriber has subscribed to the category
             if (subscriber.CategoryNodeIds.Contains(Convert.ToInt32(categoryNodeId)))
             {
                 shouldBeAdded = true;

                 // News item matched and will be sent. Stop the loop.
                 break;
             }
             else
             {
                 shouldBeAdded = false;
             }
         }

         if (!shouldBeAdded)
         {
             // The news item did not match both a city and a category and should not be sent
             nodesToSend.Remove(newsItem);
         }
    }

    if (nodesToSend.Count > 0)
    {
        result.Add(subscriber.Email, nodesToSend);
    }  
}

// Ensure that we process the request only if there are any subscribers to send mails to
if (result.Count > 0)
{
    foreach (var res in result)
    { 
        // StringBuilder to build markup for newsletter
        StringBuilder sb = new StringBuilder();

        // Build markup
        foreach (var newsItem in res.Value)
        {
            // build the markup here
        }

        // Email logic here
    }
}
Run Code Online (Sandbox Code Playgroud)

Chr*_*Fin 2

首先,您可以break尽快内心 foreach shouldBeAdde = true

您也可以使用 LINQ,但我不确定它是否会更快(但您可以使用 .AsParallel 轻松使其成为多线程):

var nodesToSend = from n in news
                 where n.GetProperties("cities").Value.Split(',')
                     .Any(c => subscriber.CityNodeIds.Contains(Convert.ToInt32(c)) &&
                 n.GetProperties("categories").Value.Split(',')
                     .Any(c => subscriber.CategoryNodeIds.Contains(Convert.ToInt32(c))
                 select n;
Run Code Online (Sandbox Code Playgroud)

完整的想法可以归结为(包括并行):

Dictionary<string, IEnumerable<Node>> result = new Dictionary<string, IEnumerable<Node>>();
foreach(var subscriber in subscribers)
{
    var nodesToSend = from n in news.AsParallel()
        where n.GetProperties("cities").Value.Split(',')
                .Any(c => subscriber.CityNodeIds.Contains(Convert.ToInt32(c)) &&
            n.GetProperties("categories").Value.Split(',')
                .Any(c => subscriber.CategoryNodeIds.Contains(Convert.ToInt32(c))
        select n;

    if (nodesToSend.Count > 0)
        result.Add(subscriber.Email, nodesToSend);
}

if (result.Count > 0)
{
    foreach (var res in result)
    {
        // Finally, create/merge the markup for the newsletter and send it as an email.
    } 
}
Run Code Online (Sandbox Code Playgroud)