Door te onderzoeken hoe de ForEach-lus in SSIS werkt (met het oog op het maken van mijn eigen om het probleem op te lossen), lijkt het erop dat de manier waarop het werkt (voor zover ik in ieder geval kon zien) is om eerst de bestandsverzameling op te sommen, voordat een masker wordt gebruikt gespecificeerd. Het is moeilijk om precies te zeggen wat er aan de hand is zonder de onderliggende code voor de ForEach-lus te zien, maar het lijkt op deze manier te gebeuren, wat resulteert in trage prestaties bij het verwerken van meer dan 100.000 bestanden.
Hoewel de oplossing van @Siva fantastisch gedetailleerd is en zeker een verbetering is ten opzichte van mijn aanvankelijke aanpak, is het in wezen hetzelfde proces, behalve het gebruik van een expressietaak om de bestandsnaam te testen, in plaats van een scripttaak (dit lijkt enige verbetering te bieden).
Dus besloot ik een totaal andere benadering te volgen en in plaats van een op bestanden gebaseerde ForEach-lus te gebruiken, de verzameling zelf op te sommen in een Scripttaak, mijn filterlogica toe te passen en vervolgens de resterende resultaten te herhalen. Dit is wat ik deed:
In mijn scripttaak gebruik ik de asynchrone DirectoryInfo.EnumerateFiles
methode, wat de aanbevolen benadering is voor grote bestandsverzamelingen, omdat het streaming mogelijk maakt, in plaats van te moeten wachten tot de hele verzameling is gemaakt voordat enige logica wordt toegepast.
Hier is de code:
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;
}
Dus ik som de verzameling op, pas mijn logica toe wanneer bestanden worden ontdekt en voeg onmiddellijk het bestandspad toe aan mijn lijst voor uitvoer. Eenmaal voltooid, wijs ik dit vervolgens toe aan een SSIS-objectvariabele met de naam ActiveFilenames die ik zal gebruiken als verzameling voor mijn ForEach-lus.
Ik heb de ForEach-lus geconfigureerd als een ForEach From Variable Enumerator , die zich nu herhaalt over een veel kleinere verzameling (nagefilterde List<string>
vergeleken met wat ik alleen maar kan aannemen was een ongefilterde List<FileInfo>
of iets dergelijks in SSIS' ingebouwde ForEach File Enumerator .
Dus de taken in mijn lus kunnen gewoon worden gewijd aan het verwerken van de gegevens, omdat deze al zijn gefilterd voordat ze de lus raken. Hoewel het niet veel anders lijkt te doen dan mijn oorspronkelijke pakket of het voorbeeld van Siva, lijkt het in productie (in ieder geval voor dit specifieke geval) alsof het filteren van de verzameling en asynchroon opsommen een enorme boost geeft ten opzichte van het gebruik van het ingebouwde ForEach-bestand Teller.
Ik ga verder met het onderzoeken van de ForEach-luscontainer en kijk of ik deze logica kan repliceren in een aangepaste component. Als ik dit werkend krijg, zal ik een link in de reacties plaatsen.