यह जांचने से कि एसएसआईएस में फॉरएच लूप कैसे काम करता है (इस मुद्दे को हल करने के लिए अपना खुद का निर्माण करने के लिए) ऐसा लगता है कि जिस तरह से यह काम करता है (जहां तक मैं वैसे भी देख सकता था) किसी भी मुखौटा से पहले फ़ाइल संग्रह को पहले गिनना है निर्दिष्ट। ForEach लूप के लिए अंतर्निहित कोड को देखे बिना वास्तव में क्या हो रहा है, यह बताना कठिन है, लेकिन ऐसा लगता है कि यह इस तरह से कर रहा है, जिसके परिणामस्वरूप 100k से अधिक फ़ाइलों के साथ काम करते समय धीमा प्रदर्शन होता है।
जबकि @ शिव का समाधान काल्पनिक रूप से विस्तृत है और निश्चित रूप से मेरे प्रारंभिक दृष्टिकोण में सुधार है, यह अनिवार्य रूप से सिर्फ एक ही प्रक्रिया है, एक स्क्रिप्ट कार्य के बजाय फ़ाइल नाम का परीक्षण करने के लिए एक अभिव्यक्ति कार्य का उपयोग करने के अलावा (यह कुछ सुधार की पेशकश करता प्रतीत होता है)।पी>
इसलिए, मैंने एक पूरी तरह से अलग दृष्टिकोण लेने का फैसला किया और फ़ाइल-आधारित ForEach लूप का उपयोग करने के बजाय, एक स्क्रिप्ट कार्य में संग्रह की गणना करें, मेरे फ़िल्टरिंग तर्क को लागू करें, और फिर शेष परिणामों पर पुनरावृति करें। मैंने यही किया:
मेरे स्क्रिप्ट कार्य में, मैं एसिंक्रोनस DirectoryInfo.EnumerateFiles
. का उपयोग करता हूं विधि, जो बड़े फ़ाइल संग्रहों के लिए अनुशंसित दृष्टिकोण है, क्योंकि यह किसी भी तर्क को लागू करने से पहले पूरे संग्रह के निर्माण की प्रतीक्षा करने के बजाय स्ट्रीमिंग की अनुमति देता है।
यह रहा कोड:
public void Main()
{
string sourceDir = Dts.Variables["SourceDirectory"].Value.ToString();
int minJobId = (int)Dts.Variables["MinIndexId"].Value;
//Enumerate file collection (using Enumerate Files to allow us to start processing immediately
List<string> activeFiles = new List<string>();
System.Threading.Tasks.Task listTask = System.Threading.Tasks.Task.Factory.StartNew(() =>
{
DirectoryInfo dir = new DirectoryInfo(sourceDir);
foreach (FileInfo f in dir.EnumerateFiles("*.txt"))
{
FileInfo file = f;
string filePath = file.FullName;
string fileName = filePath.Substring(filePath.LastIndexOf("\\") + 1);
int jobId = Convert.ToInt32(fileName.Substring(0, fileName.IndexOf(".txt")));
if (jobId > minJobId)
activeFiles.Add(filePath);
}
});
//Wait here for completion
System.Threading.Tasks.Task.WaitAll(new System.Threading.Tasks.Task[] { listTask });
Dts.Variables["ActiveFilenames"].Value = activeFiles;
Dts.TaskResult = (int)ScriptResults.Success;
}
इसलिए, मैं संग्रह की गणना करता हूं, फाइलों की खोज के रूप में अपने तर्क को लागू करता हूं और तुरंत आउटपुट के लिए मेरी सूची में फ़ाइल पथ जोड़ता हूं। एक बार पूरा हो जाने पर, मैं इसे ActiveFilenames . नामक एक SSIS ऑब्जेक्ट वैरिएबल को असाइन करता हूं जिसे मैं अपने ForEach लूप के संग्रह के रूप में उपयोग करूंगा।
मैंने ForEach लूप को Variable Enumerator से प्रत्येक के लिए . के रूप में कॉन्फ़िगर किया है , जो अब बहुत छोटे संग्रह पर पुनरावृति करता है (पोस्ट-फ़िल्टर्ड List<string>
जो मैं केवल मान सकता हूं उसकी तुलना में एक अनफ़िल्टर्ड List<FileInfo>
. था या SSIS के अंतर्निर्मित प्रत्येक फ़ाइल गणक के लिए . में कुछ ऐसा ही है ।
तो मेरे लूप के अंदर के कार्य केवल डेटा को संसाधित करने के लिए समर्पित हो सकते हैं, क्योंकि यह लूप को मारने से पहले ही फ़िल्टर किया जा चुका है। यद्यपि यह मेरे प्रारंभिक पैकेज या शिव के उदाहरण के लिए बहुत अलग नहीं लगता है, उत्पादन में (इस विशेष मामले के लिए, वैसे भी) ऐसा लगता है कि संग्रह को फ़िल्टर करना और असीमित रूप से गणना करना ForEach फ़ाइल में निर्मित का उपयोग करने पर भारी बढ़ावा देता है गणक।
मैं ForEach लूप कंटेनर की जांच जारी रखूंगा और देखूंगा कि क्या मैं इस तर्क को एक कस्टम घटक में दोहरा सकता हूं। अगर मुझे यह काम मिल गया तो मैं टिप्पणियों में एक लिंक पोस्ट करूंगा।