SqlBulkCopy.WriteToServer () continua a ricevere "la connessione è chiusa"

c# sqlbulkcopy sql-server

Domanda

Questo non ha senso per me, ma forse qualcuno con gli occhi più acuti può individuare il problema.

Ho un servizio di Windows che utilizza FileSystemWatcher. Elabora alcuni file e carica i dati in un database MSSQL. Funziona perfettamente sulla mia macchina - distaccato da Visual Studio (cioè non di debug) e in esecuzione come servizio. Se copia questo codice compilato sul nostro server e lo fai puntare allo stesso database e persino agli stessi file (!), Ottengo questo errore ogni volta:

System.InvalidOperationException: Invalid operation. The connection is closed.
   at System.Data.SqlClient.SqlConnection.GetOpenTdsConnection()
   at System.Data.SqlClient.SqlBulkCopy.CopyBatchesAsyncContinuedOnError(Boolean cleanupParser)
   at System.Data.SqlClient.SqlBulkCopy.<>c__DisplayClass30.<CopyBatchesAsyncContinuedOnSuccess>b__2c()
   at System.Data.SqlClient.AsyncHelper.<>c__DisplayClass9.<ContinueTask>b__8(Task tsk)

Ho provato a puntare il mio codice locale ai file del server e funziona perfettamente. . Net 4.5.1 è su entrambe le macchine. I servizi sono entrambi in esecuzione nello stesso utente del dominio. È sconcertante. Forse c'è qualcosa che non capisco su SqlBulkCopy.WriteToServerAsync ()? Condivide automaticamente connessioni o qualcosa del genere? Si chiude tra una chiamata o qualcosa del genere? Ecco il codice rilevante:

private static void ProcessFile(FileInfo fileInfo)
{
    using (var bulkCopy = new SqlBulkCopy("Data Source=myserver;Initial Catalog=mydb;Persist Security Info=True;User ID=myusr;Password=mypwd;")
    using (var objRdr = ObjectReader.Create(ReadLogFile(fileInfo)
                                    .Where(x => !string.IsNullOrEmpty(x.Level)),
                                         "Id", "AppId", "AppDomain", "AppMachine",
                                         "LocalDate", "UtcDate", "Thread", "Level", "Logger", "Usrname",
                                         "ClassName", "MethodName", "LineNo", "Message", "Exception",
                                         "StackTrace", "Properties"))
    {
        bulkCopy.DestinationTableName = "EventLog";
        bulkCopy.BulkCopyTimeout = 600;
        bulkCopy.EnableStreaming = true;
        bulkCopy.BatchSize = AppConfig.WriteBatchSize;
        bulkCopy.WriteToServerAsync(objRdr).ContinueWith(t =>
            {
                if (t.Status == TaskStatus.Faulted)
                {
                    CopyToFailedDirectory(fileInfo);
                    _log.Error(
                        string.Format(
                            "Error copying logs to database for file {0}. File has been copied to failed directory for inspection.",
                            fileInfo.FullName), t.Exception.InnerException ?? t.Exception);
                    Debug.WriteLine("new handle error {0}",
                                    (t.Exception.InnerException ?? t.Exception).Message);
                }
                if (t.Status == TaskStatus.RanToCompletion)
                {
                    _log.InfoFormat("File {0} logs have been copied to database.", fileInfo.FullName);
                    Debug.WriteLine("Yay, finished {0}!", fileInfo.Name);
                }
                // if this is the last one, delete the original file
                if (t.Status == TaskStatus.Faulted || t.Status == TaskStatus.RanToCompletion)
                {
                    Debug.WriteLine("deleting file {0}", fileInfo.Name);
                    PurgeFile(fileInfo);
                }
            });
    }
}

Note di coppia nel caso in cui chiedi:

  • ObjectReader è un'implementazione di IDMeadReader FastMember . PAZZO veloce. Legge il file in oggetti personalizzati con le proprietà che vedi elencate.
  • Genera l'errore per ogni singolo file.
  • Di nuovo, questo funziona sulla mia macchina, sia come servizio che come app per console. Ho persino funzionato una volta sul server. Ha gettato l'errore e non ha mai funzionato di nuovo.

Qualche idea?

Risposta accettata

Sembra un problema con Async.

Per favore fatemi sapere se ho torto, ma quello che ho notato è che avete il vostro SqlBulkCopy e ObjectReader in una istruzione using che è grande, tuttavia, si sta facendo tutta l'elaborazione in modo asincrono. Una volta, lo chiami e inizia a fare il lavoro, le tue istruzioni d' using stanno eliminando i tuoi oggetti e uccideranno anche la tua connessione.

La cosa strana è che suona come funziona a volte, ma forse diventa solo una condizione di gara a quel punto.


Risposta popolare

Questo mi sembra un bug nell'implementazione di SqlBulkCopy. Se esegui contemporaneamente un (grande) numero di copie di massa in attività separate, disattiva la connessione di rete e attiva una raccolta dati inutili completa, questa eccezione verrà generata sul thread del finalizzatore del GC. È completamente inevitabile.

Ciò non dovrebbe accadere perché si sta continuando l'attività WriteToServerAsync e si gestisce l'errore. Ma nell'implementazione, in caso di errore, iniziano una nuova attività che non continuano o attendono.

Questo sembra ancora un bug in .NET 4.6.2.

L'unica correzione che posso vedere è iscriversi a TaskScheduler.UnobservedTaskException e cercare qualcosa nello stacktrace che identifica il problema. A proposito, non è una correzione, è un trucco.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché