2009-06-04 11 views
0

J'ai implémenté un générateur de fichier csv qui prend en charge un document xml lui applique une transformation xsl et l'ajoute à un fichier.Obtention d'une IOException sur plusieurs écritures dans un fichier

public class CsvBatchPrinter : BaseBatchPrinter 
{ 
    public CsvBatchPrinter() : base(".csv") 
    { 
     RemoveDiatrics = false; 
    } 

    protected override void PrintDocuments(System.Collections.Generic.List<XmlDocument> documents, string xsltFileName, string directory, string tempImageDirectory) 
    { 
     base.PrintDocuments(documents, xsltFileName, directory, tempImageDirectory); 

     foreach (var file in new DirectoryInfo(tempImageDirectory).GetFiles()) 
     { 
      var destination = directory + file.Name; 
      if (!File.Exists(destination)) 
       file.CopyTo(destination); 
     } 
    } 

    protected override void PrintDocument(XmlDocument document, string xsltFileName, string directory, string tempImageDirectory) 
    { 
     StringUtils.EscapeQuotesInXmlNode(document); 
     if (RemoveDiatrics) 
     { 
      var docXml = StringUtils.RemoveDiatrics(document.OuterXml); 
      document = new XmlDocument(); 
      document.LoadXml(docXml); 
     } 

     using (var writer = new StreamWriter(string.Format("{0}{1}{2}", directory, "batch", FileExtension), true, Encoding.ASCII)) 
     { 
      Transform(document, xsltFileName, writer); 
     } 
    } 

    public bool RemoveDiatrics { get; set; } 
} 

J'ai un grand nombre de documents XML à ajouter à ce fichier csv et après plusieurs appels à, il jette de temps en temps une IOException The process cannot access the file 'batch.csv' because it is being used by another process.

Serait-ce une sorte de problème de verrouillage?

Pourrait-il être solutionné par:

lock(this) 
{ 
    using (var writer = new StreamWriter(string.Format("{0}{1}{2}", directory, "batch", FileExtension), true, Encoding.ASCII)) 
    { 
     Transform(document, xsltFileName, writer); 
    } 
} 

EDIT:

Voici ma trace de la pile:

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, Int32 bufferSize, FileOptions options) 
at System.IO.StreamWriter.CreateFile(String path, Boolean append) 
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding, Int32 bufferSize) 
at System.IO.StreamWriter..ctor(String path, Boolean append, Encoding encoding) 
at Receipts.Facade.Utilities.BatchPrinters.CsvBatchPrinter.PrintDocument(XmlDocument document, String xsltFileName, String directory, String tempImageDirectory) in CsvBatchPrinter.cs:line 37 
at Receipts.Facade.Utilities.BatchPrinters.BaseBatchPrinter.PrintDocuments(List`1 documents, String xsltFileName, String directory, String tempImageDirectory) in BaseBatchPrinter.cs:line 30 
at Receipts.Facade.Utilities.BatchPrinters.CsvBatchPrinter.PrintDocuments(List`1 documents, String xsltFileName, String directory, String tempImageDirectory) in CsvBatchPrinter.cs:line 17 
at Receipts.Facade.Utilities.BatchPrinters.BaseBatchPrinter.Print(List`1 documents, String xsltFileName, String destinationDirectory, String tempImageDirectory) in BaseBatchPrinter.cs:line 23 
at Receipts.Facade.Modules.FinanceDocuments.FinanceDocumentActuator`2.printXmlFiles(List`1 xmlDocuments, String tempImagesDirectory) in FinanceDocumentActuator.cs:line 137 

et ma classe de base:

public abstract class BaseBatchPrinter : IBatchPrinter 
{ 
    private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
    protected readonly string FileExtension; 

    protected BaseBatchPrinter(string fileExtension) 
    { 
     FileExtension = fileExtension; 
    } 

    public void Print(List<XmlDocument> documents, string xsltFileName, string destinationDirectory, string tempImageDirectory) 
    { 
     Log.InfoFormat("Printing to directory: {0}", destinationDirectory); 
     PrintDocuments(documents, xsltFileName, destinationDirectory, tempImageDirectory); 
    } 

    protected virtual void PrintDocuments(List<XmlDocument> documents, string xsltFileName, string directory, string tempImageDirectory) 
    { 
     foreach (var document in documents) 
     { 
      PrintDocument(document, xsltFileName, directory, tempImageDirectory); 
     } 
    } 

    /// <summary> 
    /// Needs to Call Transform(XmlDocument document, string xsltFileName, string directory) 
    /// </summary> 
    protected abstract void PrintDocument(XmlDocument document, string xsltFileName, string directory, string tempImageDirectory); 

    protected void Transform(XmlDocument document, string xsltFileName, StreamWriter writer) 
    { 
     //TODO: look into XslCompiledTransform to replace the XslTransform 
     var xslTransform = new XslTransform(); 
     xslTransform.Load(xsltFileName); 
     xslTransform.Transform(createNavigator(document), null, writer); 
    } 

    protected string CreateFileName(string directory, XmlDocument doc) 
    { 
     var conId = createNavigator(doc).SelectSingleNode(Config.SELECT_CONSTITUENT_ID_XPATH).Value; 
     return string.Format(@"{0}{1}{2}", directory, conId, FileExtension.IndexOf('.') > -1 ? FileExtension : "." + FileExtension); 
    } 

    protected XPathNavigator createNavigator(XmlDocument document) 
    { 
     return document.DocumentElement == null ? document.CreateNavigator() : document.DocumentElement.CreateNavigator(); 
    } 
} 

Cheers.

+1

Vous savez que vous n'appelez pas votre méthode CreateFilename() correctement?Si c'est l'intention alors peut-être, d'expérience récente, j'ajouterais un .Close() dans le bloc using si l'intention est d'écrire chaque doc dans le même fichier, même si Dispose() devrait être appelé quand sortir de la portée , qui devrait fermer le fichier. Ne coûte rien à essayer et vous prendra quelques minutes –

+0

Simon, vous devriez ajouter cela comme une réponse .... – zonkflut

+0

ajoutant le supplément .Close() ne fonctionnait pas. – zonkflut

Répondre

0

Salut tous merci pour vos réponses. J'ai trouvé une solution qui fonctionne pour moi. Un peu hacky mais ça fait l'affaire.

protected override void PrintDocument(XmlDocument document, string xsltFileName, string directory, string tempImageDirectory) 
{ 
    StringUtils.EscapeQuotesInXmlNode(document); 
if (RemoveDiatrics) 
{ 
    var docXml = StringUtils.RemoveDiatrics(document.OuterXml); 
    document = new XmlDocument(); 
    document.LoadXml(docXml); 
} 

IOException ex = null; 
for (var attempts = 0; attempts < 10; attempts++) 
{ 
    ex = tryWriteToFile(document, directory, xsltFileName); 
    if (ex == null) 
     break; 
} 

if (ex != null) 
    throw new ApplicationException("Cannot write to file", ex); 
} 

private IOException tryWriteToFile(XmlDocument document, string directory, string xsltFileName) 
{ 
try 
{ 
    using (var writer = new StreamWriter(new FileStream(string.Format("{0}{1}{2}", directory, "batch", FileExtension), FileMode.Append, FileAccess.Write, FileShare.Read), Encoding.ASCII)) 
    { 
     Transform(document, xsltFileName, writer); 
    writer.Close(); 
    } 
    return null; 
} 
catch (IOException e) 
{ 
    return e; 
} 
} 

Fondamentalement, l'idée est d'essayer de l'exécuter deux fois et si le problème persiste jeter l'erreur.

Obtient-moi le problème

0

Une seule chose peut causer cela. Un autre processus écrit dans le fichier. L'autre processus pourrait même être le vôtre.

Je vous suggère d'ajouter des blocs try/catch autour des zones pouvant écrire ce fichier. Dans le bloc catch, lancer une nouvelle exception, en ajoutant le chemin complet du fichier:

var filePath = string.Format("{0}{1}{2}", directory, "batch", FileExtension); 
try { 
    using (var writer = new StreamWriter(filepath, true, Encoding.ASCII)) { 
     Transform(...); 
    } 
} 
catch (IOException ex) { 
    throw new Exception(
     String.Format("Exception while transforming file {0}", filePath), 
     ex); 
} 

Poster la complète exception que vous recevez, y compris toute trace InnerException et pile.

Vous voulez également vous assurer que votre copie de fichier pourrait même écrire dans batch.csv.

+0

J'ai supprimé le chemin d'accès complet du fichier était par souci de concision. Et oui, le seul processus qui accédait au fichier était mon processus, qui utilise un seul thread. – zonkflut

+0

Vous pouvez avoir un seul thread, mais vous avez une pile, et vous avez des méthodes surchargées. Essayez ma suggestion et regardez la trace de la pile. –

+0

Je suis presque à court d'idées, mais est-il possible que CreateFileName puisse créer le même nom de fichier que celui utilisé par votre méthode PrintDocument surchargée? En outre, procurez-vous une copie de Process Monitor depuis http://technet.microsoft.com/en-us/sysinternals/default.aspx et observez votre programme lorsqu'il s'exécute. –

0

Si vous le souhaitez, que diriez-vous d'essayer les mods suivants pour le code pour aider davantage à trouver la faute que comme une solution permanente.

Ajouter une surcharge pour imprimer un document qui prend un paramètre StreamWriter. base.PrintDocuments Modifier comme suit:

using (var writer = new StreamWriter (string.Format ("{0}{1}{2}", directory, "batch", FileExtension), true, Encoding.ASCII)) 
{ 
    foreach (var document in documents) 
    { 
     PrintDocument (document, xsltFileName, directory, tempImageDirectory, writer); 
    } 
} 

protected override void PrintDocument (XmlDocument document, string xsltFileName, string directory, string tempImageDirectory, StreamWriter writer) 
{ 
    if (RemoveDiatrics) 
    { 
     var docXml = document.OuterXml; 
     document = new XmlDocument (); 
     document.LoadXml (docXml); 
    } 

    Transform (document, xsltFileName, writer);   
} 

Si cela ne fonctionne pas ... toujours le pessimiste ... Je regarderais Transform comme un coupable possible, mais je sais que cela ne fait rien avec le TextWriter autre que Eh bien, écrivez avec. Quoi qu'il en soit, donnez un coup de feu si vous avez le temps, la création de l'écrivain une fois peut-être la solution (solution rapide), laissez-nous savoir si cela fonctionne.