Dall'indagine su come funziona il ciclo ForEach in SSIS (al fine di crearne uno per risolvere il problema), sembra che il modo in cui funziona (per quanto ho potuto vedere comunque) sia di enumerare prima la raccolta di file, prima che qualsiasi maschera sia specificato. È difficile dire esattamente cosa sta succedendo senza vedere il codice sottostante per il ciclo ForEach, ma sembra che lo faccia in questo modo, con conseguente rallentamento delle prestazioni quando si tratta di file oltre 100.000.
Sebbene la soluzione di @Siva sia straordinariamente dettagliata e sicuramente un miglioramento rispetto al mio approccio iniziale, è essenzialmente lo stesso processo, tranne l'utilizzo di un'attività di espressione per testare il nome del file, piuttosto che un'attività di script (questo sembra offrire qualche miglioramento).
Quindi, ho deciso di adottare un approccio completamente diverso e piuttosto che utilizzare un ciclo ForEach basato su file, enumerare personalmente la raccolta in un'attività di script, applicare la mia logica di filtro e quindi scorrere i risultati rimanenti. Questo è quello che ho fatto:
Nella mia attività di script, utilizzo il DirectoryInfo.EnumerateFiles
asincrono metodo, che è l'approccio consigliato per raccolte di file di grandi dimensioni, in quanto consente lo streaming, anziché dover attendere la creazione dell'intera raccolta prima di applicare qualsiasi logica.
Ecco il codice:
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;
}
Quindi, enumero la raccolta, applicando la mia logica quando i file vengono scoperti e aggiungendo immediatamente il percorso del file al mio elenco per l'output. Una volta completato, lo assegno a una variabile oggetto SSIS denominata ActiveFilenames che userò come raccolta per il mio ciclo ForEach.
Ho configurato il ciclo ForEach come ForEach da enumeratore di variabili , che ora esegue l'iterazione su una raccolta molto più piccola (List<string>
post-filtrato rispetto a quello che posso solo supporre fosse un List<FileInfo>
non filtrato o qualcosa di simile nel ForEach File Enumerator integrato in SSIS .
Quindi le attività all'interno del mio ciclo possono essere semplicemente dedicate all'elaborazione dei dati, poiché sono già state filtrate prima di colpire il ciclo. Sebbene non sembri fare molto diverso dal mio pacchetto iniziale o dall'esempio di Siva, in produzione (per questo caso particolare, comunque) sembra che filtrare la raccolta e l'enumerazione in modo asincrono fornisca un enorme aumento rispetto all'utilizzo del file ForEach integrato Enumeratore.
Continuerò a esaminare il contenitore del ciclo ForEach e vedrò se riesco a replicare questa logica in un componente personalizzato. Se riesco a farlo funzionare, posterò un link nei commenti.