2010-06-08 6 views
3

J'ai un service Web qui vérifie un dictionnaire pour voir si un fichier existe et s'il existe, il lit le fichier, sinon il enregistre dans le fichier. Ceci provient d'une application Web. Je me demande quelle est la meilleure façon de le faire parce que je reçois occasionnellement une exception FileNotFoundException si le même fichier est accédé en même temps. est ici les parties pertinentes du code:Enregistrement de fichiers dans un fichier et lecture du fichier bfile

String signature; 
signature = "FILE," + value1 + "," + value2 + "," + value3 + "," + value4;  // this is going to be the filename 

string result;    
MultipleRecordset mrSummary = new MultipleRecordset(); // MultipleRecordset is an object that retrieves data from a sql server database 

if (mrSummary.existsFile(signature)) 
{     
    result = mrSummary.retrieveFile(signature);     
} 
else 
{     
    result = mrSummary.getMultipleRecordsets(System.Configuration.ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString.ToString(), value1, value2, value3, value4); 
    mrSummary.saveFile(signature, result); 
} 

Voici le code pour voir si le fichier existe déjà:

private static Dictionary<String, bool> dict = new Dictionary<String, bool>(); 

public bool existsFile(string signature) 
{      
    if (dict.ContainsKey(signature)) 
    { 
     return true; 
    } 
    else 
    { 
     return false; 
    }         
} 

Voici ce que je l'utilise pour récupérer si elle existe déjà:

try 
{        

    byte[] buffer; 
    FileStream fileStream = new FileStream(@System.Configuration.ConfigurationManager.AppSettings["CACHEPATH"] + filename, FileMode.Open, FileAccess.Read, FileShare.Read); 
     try 
     { 
     int length = 0x8000; // get file length 
     buffer = new byte[length];   // create buffer 
     int count;       // actual number of bytes read 
     JSONstring = ""; 
     while ((count = fileStream.Read(buffer, 0, length)) > 0) 
     { 
      JSONstring += System.Text.ASCIIEncoding.ASCII.GetString(buffer, 0, count); 
     } 
     } 
     finally 
     { 
     fileStream.Close(); 
     } 
} 
catch (Exception e) 
{ 

    JSONstring = "{\"error\":\"" + e.ToString() + "\"}";     
} 

Si le fichier n'existe pas, il enregistre le fichier JSON dans le fichier:

try 
{     
    if (dict.ContainsKey(filename) == false) 
    { 
     dict.Add(filename, true); 
    } 
    else 
    { 
     this.retrieveFile(filename, ipaddress); 
    } 

} 
catch 
{ 


} 


try 
{ 
    TextWriter tw = new StreamWriter(@System.Configuration.ConfigurationManager.AppSettings["CACHEPATH"] + filename); 
    tw.WriteLine(JSONstring); 
    tw.Close(); 
} 
catch { 

} 

Voici les détails à l'exception parfois je reçois de l'exécution du code ci-dessus:

System.IO.FileNotFoundException: Could not find file 'E:\inetpub\wwwroot\cache\FILE,36,36.25,14.5,14.75'. 
File name: 'E:\inetpub\wwwroot\cache\FILE,36,36.25,14.5,14.75' 
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 
    at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy) 
    at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share) 
    at com.myname.business.MultipleRecordset.retrieveFile(String filename, String ipaddress) 
+2

Joli ratio élevé WTF/LOC, Je dois dire ... – SWeko

Répondre

3

Vous obtenez un FileNotFoundException parce que vous ajoutez le nom de fichier dans le dictionnaire avant d'écrire le fichier. Ainsi, des opérations simultanées sur le même fichier provoqueront ce problème.

L'ajout au dictionnaire après l'écriture créera seulement un nouveau problème cependant: il commencera à essayer d'écrire dans le fichier simultanément (et échouer lamentablement). Si la performance est critique, je voudrais changer mon dictionnaire en Dictionary<string, object> et utiliser la valeur comme un objet de synchronisation au niveau du fichier. Vous devrez également ajouter un objet de synchronisation distinct pour vérifier et ajouter au dictionnaire lui-même.

Cependant, il s'agit probablement d'une surcharge, et il faudrait utiliser manuellement Monitor.Enter et Monitor.Exit. Voici une mise en œuvre un peu plus simple:

static HashSet<string> files = new HashSet<string>(); 
static object syncRoot = new object(); 

void Whatever(string filename, string ipaddress) 
{ 
    bool fileFound; 

    lock (syncRoot) 
    { 
     fileFound = files.Contains(filename); 
     if (!fileFound) 
     { 
      files.Add(filename); 
      // Code to write file here 
     } 
    } 

    if (fileFound) 
    { 
     retrieveFile(filename, ipaddress); 
    } 
} 

Lorsqu'une écriture est fait, la performance serait un peu sous-optimale, car il bloque toutes les opérations de lecture jusqu'à ce que l'écriture est terminée. Si la majorité des opérations sont lues, ce n'est pas un problème cependant.

J'ai changé votre dictionnaire en HashSet dans cet exemple, car vous semblez utiliser uniquement la clé, et non la valeur.

+0

Merci - votre mise en œuvre fonctionne bien. – user220511

1

C'est un problème de threading.

Le FileNotFoundException se passe sur le fichier n'existe pas encore cette ligne

FileStream fileStream = new FileStream(System.Configuration.ConfigurationManager.AppSettings["CACHEPATH"] + filename, FileMode.Open, FileAccess.Read, FileShare.Read); 

.

Vous appelez

dict.Add(filename, true); 

avant de créer le fichier

TextWriter tw = new StreamWriter(@System.Configuration.ConfigurationManager.AppSettings["CACHEPATH"] + filename); 
tw.WriteLine(JSONstring); 
tw.Close(); 

qui fait votre test pour l'existence de fichier inexact

public bool existsFile(string signature)  
{        
    if (dict.ContainsKey(signature))  
    {  
     return true;  
    }  
    else  
    {  
     return false;  
    }          
} 
+0

Ce n'est pas tout le problème cependant. Changer l'ordre de ces deux opérations entraînera un nouveau problème potentiel (voir ma réponse). – Thorarin