От проучването как работи цикълът ForEach в SSIS (с оглед на създаването на моя собствена за разрешаване на проблема) изглежда, че начинът, по който работи (доколкото успях да видя така или иначе), е първо да изброя колекцията от файлове, преди да бъде поставена каквато и да е маска посочени. Трудно е да се каже какво точно се случва, без да се види базовият код за цикъла ForEach, но изглежда, че го прави по този начин, което води до ниска производителност при работа с над 100k файла.
Въпреки че решението на @Siva е фантастично подробно и определено е подобрение спрямо първоначалния ми подход, то по същество е същият процес, с изключение на използването на задача за изразяване за тестване на името на файла, а не задача за скрипт (това изглежда предлага известно подобрение).
Така че реших да възприема напълно различен подход и вместо да използвам базиран на файл цикъл 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;
}
И така, аз изброявам колекцията, прилагайки моята логика при откриване на файлове и незабавно добавяйки пътя на файла към моя списък за изход. След като завърша, присвоявам това на променлива на SSIS Object с име ActiveFilenames който ще използвам като колекция за моя цикъл ForEach.
Конфигурирах цикъла ForEach като ForEach From Variable Enumerator , който сега обхожда много по-малка колекция (Постфилтриран List<string>
в сравнение с това, което мога само да предположа, че е нефилтриран List<FileInfo>
или нещо подобно във вградения ForEach File Enumerator на SSIS .
Така че задачите в моя цикъл могат просто да бъдат посветени на обработката на данните, тъй като те вече са били филтрирани, преди да попаднат в цикъла. Въпреки че изглежда не се различава много от моя първоначален пакет или от примера на Siva, в производството (за този конкретен случай, така или иначе) изглежда, че филтрирането на колекцията и асинхронното изброяване осигурява огромен тласък в сравнение с използването на вградения ForEach файл Изброител.
Ще продължа да изследвам контейнера за цикъл ForEach и ще видя дали мога да репликирам тази логика в персонализиран компонент. Ако проработя, ще публикувам връзка в коментарите.