2015-09-15 11 views
6

Nel mio metodo di azione WebApi, voglio creare/sovrascrivere una cartella utilizzando questo codice:Utilizzando Directory.Delete() e Directory.CreateDirectory() per sovrascrivere una cartella

string myDir = "..."; 
if(Directory.Exists(myDir)) 
{ 
    Directory.Delete(myDir, true); 
} 
Directory.CreateDirectory(myDir); 

// 1 - Check the dir 
Debug.WriteLine("Double check if the Dir is created: " + Directory.Exists(myDir)); 

// Some other stuff here... 

// 2 - Check the dir again 
Debug.WriteLine("Check again if the Dir still exists: " + Directory.Exists(myDir)); 

Problema

Stranamente, a volte proprio dopo aver creato la directory, la directory non esiste!

A volte quando si controlla la dir per la prima volta (dove è il numero 1); Directory.Exist() restituisce true, altre volte false. Lo stesso accade quando si controlla la dir per la seconda volta (dove è il numero 2).

Note

  • Nessuno di questa parte del codice di gettare alcuna eccezione.
  • Può riprodurlo solo quando si pubblica il sito Web sul server. (Windows server 2008)
  • Accade quando si accede alla stessa cartella.

Domande

  • Si tratta di una condizione di competizione problema di concorrenza?
  • Non WebApi o il sistema operativo gestisce la concorrenza?
  • È questo il modo corretto di sovrascrivere una cartella?
  • Devo bloccare i file manualmente quando abbiamo molte richieste API sullo stesso file?

o, in generale:

  • Qual è il motivo di questo strano comportamento?

UPDATE:

  • Utilizzando DirectoryInfo e Refresh() invece di Directory non risolve il problema.

  • accade solo quando l'opzione ricorsiva di Elimina è true. (e la directory non è vuota).

+0

La directory è una condivisione di file (vale a dire il disco su una macchina diversa dal server Web)? – jlew

+0

@jlew No, si trova sullo stesso computer del server web. –

risposta

0

Suona come una condizione di gara per me. Non sei sicuro del perché - non hai fornito abbastanza dettagli - ma quello che puoi fare è racchiudere tutto nella dichiarazione lock() e vedere se il problema è scomparso. Di sicuro questa soluzione non è pronta per la produzione, è solo un modo veloce per controllare. Se è davvero una condizione di gara, devi ripensare al tuo approccio per la riscrittura delle cartelle. Può essere creata la cartella "GUID" e al termine dell'operazione, aggiornare DB con il GUID più recente in modo che punti alla cartella più recente? ..

+0

Aggiunta questa riga al codice: 'Debug.WriteLine (System.Threading.Thread.CurrentThread.ManagedThreadId)'. Tutto sembra accadere nella stessa discussione. Anche provato a bloccare, pur avendo lo stesso problema. –

+0

Inoltre, è già una cartella GUID, in alcuni casi ho bisogno di sovrascrivere questa cartella GUID, cosa che succede. –

7

Molte operazioni sul filesystem non sono sincrone su alcuni file system (nel caso di Windows - NTFS). Prendiamo ad esempio RemoveDirectory chiamata (che è chiamato da Directory.DeleteDirectory ad un certo punto):

La funzione RemoveDirectory segna una directory per l'eliminazione su una stretta. Pertanto, la directory non viene rimossa finché non viene chiuso l'ultimo handle della directory.

Come vedete, non cancellerà realmente la directory finché tutti gli handle non saranno chiusi, ma Directory.DeleteDirectory sarà completa. Nel tuo caso è anche molto probabile che si tratti di un problema di concorrenza - la directory non viene realmente creata durante l'esecuzione di Directory.Exists.

Quindi, basta controllare periodicamente ciò che è necessario e non considerare le chiamate al filesystem in .NET come sincrone. In alcuni casi è anche possibile utilizzare FileSystemWatcher per evitare il polling.

EDIT: stavo pensando come riprodurre, ed ecco il codice:

internal class Program { 
    private static void Main(string[] args) { 
     const string path = "G:\\test_dir"; 
     while (true) {   
      if (Directory.Exists(path)) 
       Directory.Delete(path);  
      Directory.CreateDirectory(path); 
      if (!Directory.Exists(path)) 
       throw new Exception("Confirmed");     
     }    
    }   
} 

Si vede che, se tutte le chiamate filesystem erano sincrona (NET), questo codice dovrebbe funzionare senza problemi. Ora, prima di eseguire quel codice, creare una directory vuota sul percorso specificato (preferibilmente non usare SSD per questo) e aprirlo con Windows Explorer. Ora esegui il codice. Per me o getta Confermato (che riproduce esattamente il tuo problema) o getta su Directory.Delete dicendo che la directory non esiste (quasi nello stesso caso). Lo fa il 100% del tempo per me.

Ecco un altro codice che quando si esegue sulla mia macchina conferma che è certamente possibile per File.Exists per tornare vero subito dopo File.Delete chiamata:

internal class Program { 
    private static void Main(string[] args) { 
     while (true) { 
      const string path = @"G:\test_dir\test.txt"; 
      if (File.Exists(path)) 
       File.Delete(path); 
      if (File.Exists(path)) 
       throw new Exception("Confirmed"); 
      File.Create(path).Dispose(); 
     } 
    }   
} 

exception

Per fare questo, ho aperto G: \ test_dir cartella e durante l'esecuzione di questo codice ha cercato di aprire costantemente apparendo e scomparendo test.txt file. Dopo un paio di tentativi, è stata generata un'eccezione confermata (mentre non ho creato o eliminato quel file, e dopo che l'eccezione è stata lanciata, non è già presente sul filesystem). Quindi le condizioni di gara sono possibili in più casi e la mia risposta è corretta.

+0

Aggiornamento con codice di esempio per riprodurre il comportamento. – Evk

+1

L'unica volta che un'eliminazione non viene eseguita quando Elimina restituisce è quando c'è un handle aperto che ha l'eliminazione di condivisione file. Questo è estremamente raro. Questo è opt-in comportamento che quasi nessun codice utilizza. In caso contrario, le eliminazioni sono completamente sincrone. Non riesco a riprodurre questo comportamento neanche. Trovo che questa risposta sia altamente fuorviante. – usr

+1

Probabilmente il suo problema si basa su una gara. Non su FILE_SHARE_DELETE. Non so perché questa risposta sia accettata in quanto non fornisce nemmeno una soluzione. 'È un processo a più fasi e Windows non può nemmeno sapere quando sarà completato, quindi tornerà da File.Delete prima di quello. ... - la directory non viene realmente creata durante l'esecuzione di Directory.Exists. Entrambe le frasi sono false. – usr

Problemi correlati