2012-03-08 8 views
9

में एक एल्गोरिदम और/या संरचना को अनुकूलित करना मैं उस एप्लिकेशन पर काम कर रहा हूं जहां आप न्यूजलेटर की सदस्यता ले सकते हैं और चुन सकते हैं कि आप किस श्रेणी में सदस्यता लेना चाहते हैं। श्रेणियों के दो अलग-अलग सेट हैं: शहर और श्रेणियां।सी #

ईमेल भेजने पर (जो एक अनुसूचित स्वाद है), मुझे यह देखना होगा कि ईमेल भेजने से पहले मुझे कौन से शहरों और किस ग्राहक ने सदस्यता ली है। यानी, अगर मैंने अपनी पसंद के शहरों के रूप में "लंदन" और "मैनचेस्टर" की सदस्यता ली है और मेरी श्रेणियों के रूप में "खाद्य", "कपड़ा" और "इलेक्ट्रॉनिक्स" चुना है, तो मुझे केवल उन समाचार पत्र मिलेंगे जो इनसे संबंधित हैं।

संरचना इस प्रकार है:

Umbraco सीएमएस में हर newsitem पर शहरों और श्रेणियों की एक commaseparated स्ट्रिंग है (प्रभावी रूप से, इन शहरों के बाद से नोड आईडी के रूप में जमा हो जाती है और श्रेणियों Umbraco रूप में अच्छी तरह में नोड्स हैं) जब मैं एक या अधिक शहर और एक या एक से अधिक श्रेणी की सदस्यता लें, मैं कस्टम टेबल में डेटाबेस में शहर और श्रेणी नोडिड्स स्टोर करता हूं। मेरे संबंधपरक मानचित्रण इस तरह दिखता है:

enter image description here

और सारी संरचना इस तरह दिखता है:

enter image description here

मेरे लिए, यह 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. 
    } 
} 

यह काम करता है, लेकिन जब मैं तीन घोंसला वाले फोरैच लूप में हूं, तो कुछ निश्चित ग्राहकों तक पहुंचने पर मैं प्रदर्शन के बारे में थोड़ा चिंतित हूं। साथ ही, मेरे पुराने शिक्षकों को याद रखने का उपदेश है: "प्रत्येक लूप के लिए एक बेहतर संरचना है"

तो, मैं उपर्युक्त समाधान पर आपकी राय चाहूंगा, क्या ऐसी कोई चीज है जिसे दिए गए ढांचे के साथ यहां सुधार किया जा सकता है? और क्या यह समय के साथ प्रदर्शन समस्याओं का कारण बन जाएगा?

किसी भी मदद/संकेत की सराहना की जाती है! :-)

अग्रिम धन्यवाद।

समाधान

तो मैं अंत में कुछ है कि काम करता है के साथ आया था डिबगिंग और 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 
    } 
} 
+1

मैं मैं Umbraco बारे में कुछ पता नहीं है, लेकिन मैं इस सवाल के रूप में चिह्नित के रूप में यह है कि कैसे इस तरह के एक सवाल पूछने के बारे में जाने के लिए के एक * मॉडल * कहना है। – deadlyvices

+0

धन्यवाद deadlyvices :) मुझे पता है कि उपरोक्त कोड उदाहरण (और होगा!) एक से अधिक तरीकों से refactored किया जा सकता है। – bomortensen

उत्तर

2

सबसे पहले आप 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; 

पूरा थिंक तो नीचे आ जाएगा (सहित समानांतर।):

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. 
    } 
} 
+0

हाय chrfin, बहुत बहुत धन्यवाद! यह दृष्टिकोण ठोस दिखता है। मैंने इसे समानांतर विधि के साथ करने की कोशिश की, लेकिन दुर्भाग्यवश पहली जांच पर एक नलपॉइंटर अपवाद मिला: जहां एन। गेटप्रॉपर्टीज ("शहरों")। वैल्यू। स्प्लिट (',') .एनी (सी => ग्राहक। सिटीनोड आईडी.कंट्स (Convert.ToInt32 (सी)) हालांकि, asparallel विधि के बिना, सब कुछ काम करता है के रूप में काम करता है! :) फिर से धन्यवाद! – bomortensen

+0

हाय क्रिफिन ने आपके लिनक क्वेरी के साथ थोड़ा और बेवकूफ बनाने की कोशिश की और यह पता चला कि यह श्रेणियां जांचती हैं जो तय करती हैं कि कौन सी खबर भेजनी है। क्या समाचार वस्तुएं प्राप्त करने का कोई तरीका है जहां शहरों और श्रेणियां मिलती हैं? :) – bomortensen

+0

हां, असल में इसे पहले से ही करना चाहिए क्योंकि इसे तर्कसंगत रूप से अनुवादित किया जा सकता है "जहां कोई भी शहर शहर नोड्स और श्रेणी नोड्स में से किसी भी श्रेणी में है"। यदि आपको अलग-अलग परिणामों की आवश्यकता है (एसक्यूएल क्वेरी के रूप में इसके बारे में सोचें) तो आप क्वेरी के साथ बस झुका सकते हैं ... – ChrFin

0

मुझे नहीं लगता कि आप करेंगे किसी भी समय प्रदर्शन समस्याओं में भाग लें। मैं इसे छोड़ दूंगा जैसा कि आपके पास है और केवल वास्तविक प्रदर्शन समस्या में भाग लेने के बाद अनुकूलित करने का प्रयास करें और यह सत्यापित करने के लिए एक प्रोफाइलर का उपयोग किया है कि ये लूप समस्या हैं। वर्तमान में, ऐसा लगता है कि आप समयपूर्व अनुकूलन कर रहे हैं।

कहा करने के बाद कि, निम्नलिखित एक संभव अनुकूलन हो सकता है:

आप कुंजी के रूप में शहर और शब्दकोश के मूल्य के रूप में इस शहर के लिए ग्राहकों के साथ एक शब्दकोश में ग्राहक के लिए शहर से संबंध संग्रहीत कर सकती है HashSet<T> के रूप में संग्रहीत। और आप श्रेणी के लिए ग्राहक के लिए भी ऐसा ही कर सकते हैं।

अब जब आप अपना न्यूजलेटर भेजते हैं तो आप समाचार वस्तुओं पर फिर से शुरू कर सकते हैं, आप शब्दकोश का उपयोग कर शहरों के लिए ग्राहकों को पुनर्प्राप्त कर सकते हैं और आप शब्दकोश का उपयोग कर श्रेणियों के लिए ग्राहकों को पुनर्प्राप्त कर सकते हैं। अब आपको श्रेणी के ग्राहकों हैशसेट के साथ शहर के ग्राहकों हैशसेट को छेड़छाड़ करने की आवश्यकता है और नतीजतन आपके पास समाचार आइटम के लिए सभी मिलान करने वाले ग्राहक होंगे।

+0

हाय डैनियल, आपके इनपुट के लिए बहुत बहुत धन्यवाद! :) मैं निश्चित रूप से आपके सुझाव का भी प्रयास करूंगा! अधिक सुझाव विलय;) – bomortensen