Od zbadania, jak działa pętla ForEach w SSIS (z myślą o stworzeniu własnej w celu rozwiązania problemu) wydaje się, że sposób działania (o ile mogłem to zobaczyć) polega na wyliczeniu kolekcji plików, zanim jakakolwiek maska zostanie określony. Trudno powiedzieć dokładnie, co się dzieje, nie widząc kodu dla pętli ForEach, ale wydaje się, że robi to w ten sposób, co skutkuje niską wydajnością podczas obsługi ponad 100k plików.
Chociaż rozwiązanie @Siva jest fantastycznie szczegółowe i zdecydowanie stanowi poprawę w stosunku do mojego początkowego podejścia, jest to zasadniczo ten sam proces, z wyjątkiem użycia zadania wyrażenia do testowania nazwy pliku, a nie zadania skryptu (wydaje się to oferować pewną poprawę).
Postanowiłem więc przyjąć zupełnie inne podejście i zamiast używać pętli ForEach opartej na plikach, samodzielnie wyliczyć kolekcję w zadaniu skryptu, zastosować moją logikę filtrowania, a następnie wykonać iterację pozostałych wyników. Oto co zrobiłem:
W moim zadaniu skryptu używam asynchronicznego DirectoryInfo.EnumerateFiles
Metoda, która jest zalecanym podejściem w przypadku dużych kolekcji plików, ponieważ umożliwia przesyłanie strumieniowe, zamiast czekać na utworzenie całej kolekcji przed zastosowaniem jakiejkolwiek logiki.
Oto kod:
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;
}
Tak więc wyliczam kolekcję, stosując moją logikę, gdy pliki są wykrywane i natychmiast dodając ścieżkę pliku do mojej listy w celu uzyskania danych wyjściowych. Po zakończeniu przypisuję to do zmiennej obiektu SSIS o nazwie ActiveFilenames którego użyję jako kolekcji dla mojej pętli ForEach.
Skonfigurowałem pętlę ForEach jako ForEach From Variable Enumerator , który teraz iteruje po znacznie mniejszej kolekcji (po filtrowaniu List<string>
w porównaniu do tego, co mogę tylko założyć, było niefiltrowanym List<FileInfo>
lub coś podobnego we wbudowanym Enumeratorze plików ForEach .
Tak więc zadania w mojej pętli mogą być przeznaczone tylko do przetwarzania danych, ponieważ zostały one już przefiltrowane przed trafieniem w pętlę. Chociaż wydaje się, że nie różni się to zbytnio od mojego początkowego pakietu lub przykładu Siva, w produkcji (w każdym razie w tym konkretnym przypadku) wydaje się, że filtrowanie kolekcji i asynchroniczne wyliczanie zapewnia ogromny wzrost w zakresie korzystania z wbudowanego pliku ForEach Numerator.
Zamierzam kontynuować badanie kontenera pętli ForEach i sprawdzić, czy mogę replikować tę logikę w komponencie niestandardowym. Jeśli to działa, opublikuję link w komentarzach.