Après avoir enquêté sur le fonctionnement de la boucle ForEach dans SSIS (en vue de créer la mienne pour résoudre le problème), il semble que la façon dont cela fonctionne (pour autant que je puisse voir de toute façon) est d'énumérer la collection de fichiers en premier, avant tout masque est spécifié. Il est difficile de dire exactement ce qui se passe sans voir le code sous-jacent de la boucle ForEach, mais il semble que ce soit le cas, ce qui ralentit les performances lorsqu'il s'agit de plus de 100 000 fichiers.
Bien que la solution de @ Siva soit incroyablement détaillée et constitue certainement une amélioration par rapport à mon approche initiale, il s'agit essentiellement du même processus, à l'exception de l'utilisation d'une tâche d'expression pour tester le nom de fichier, plutôt qu'une tâche de script (cela semble offrir une certaine amélioration) .
J'ai donc décidé d'adopter une approche totalement différente et plutôt que d'utiliser une boucle ForEach basée sur des fichiers, d'énumérer moi-même la collection dans une tâche de script, d'appliquer ma logique de filtrage, puis de parcourir les résultats restants. Voici ce que j'ai fait :
Dans ma tâche de script, j'utilise le DirectoryInfo.EnumerateFiles
asynchrone méthode, qui est l'approche recommandée pour les grandes collections de fichiers, car elle permet le streaming, plutôt que d'avoir à attendre que la collection entière soit créée avant d'appliquer une logique.
Voici le 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;
}
Donc, j'énumère la collection, en appliquant ma logique au fur et à mesure que les fichiers sont découverts et en ajoutant immédiatement le chemin du fichier à ma liste pour la sortie. Une fois terminé, je l'attribue ensuite à une variable d'objet SSIS nommée ActiveFilenames que je vais utiliser comme collection pour ma boucle ForEach.
J'ai configuré la boucle ForEach en tant que ForEach From Variable Enumerator , qui itère maintenant sur une collection beaucoup plus petite (Post-filtered List<string>
par rapport à ce que je ne peux que supposer était un List<FileInfo>
non filtré ou quelque chose de similaire dans ForEach File Enumerator intégré à SSIS .
Ainsi, les tâches à l'intérieur de ma boucle peuvent uniquement être dédiées au traitement des données, car elles ont déjà été filtrées avant d'entrer dans la boucle. Bien que cela ne semble pas être très différent de mon package initial ou de l'exemple de Siva, en production (pour ce cas particulier, de toute façon), il semble que le filtrage de la collection et l'énumération de manière asynchrone fournissent un coup de pouce massif par rapport à l'utilisation du fichier ForEach intégré Recenseur.
Je vais continuer à enquêter sur le conteneur de boucle ForEach et voir si je peux répliquer cette logique dans un composant personnalisé. Si cela fonctionne, je publierai un lien dans les commentaires.