2009-06-04 10 views
0

xmlドキュメントを取り込むcsvファイルビルダーを実装しました。xslトランスフォームを適用してファイルに追加します。ファイルへの複数の書き込みでIOExceptionを取得する

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; } 
} 

私は、このcsvファイルに追加するXML文書の数が多いし、それを複数回呼び出した後、それは時折にIOException The process cannot access the file 'batch.csv' because it is being used by another process.

を投げ、これが問題をロックのいくつかの並べ替え可能でしょうか?

は、それをすることによって解決することでした:

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

編集:ここでは

は私のスタックトレースです:

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 

と私の基本クラス:

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(); 
    } 
} 

乾杯。

+1

CreateFilename()メソッドを正しく呼び出さないことはわかりますか?それが意図であれば、おそらく最近の経験から、スコープ外に出るときにDispose()を呼び出さなくても、同じファイルに各ドキュメントを書き込むことを意図しているならば、usingブロック内に.Close()を追加しますこれはファイルを閉じるべきです。何も試してもかまわないし、数分かかるかもしれない –

+0

Simonあなたは答えとしてそれを加えてください.... – zonkflut

+0

余分な.Close()を加えても機能しませんでした。 – zonkflut

答えて

0

ご回答いただきありがとうございます。

私は私のために働く解決策を考え出しました。少しハッキーですが、それは仕事をします。

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; 
} 
} 

基本的には、それを数回実行し、問題が解決しない場合はエラーをスローします。

問題で私を取得します

0

これを引き起こす原因は1つだけです。もう1つのプロセスはファイルへの書き込みです。他のプロセスはあなたのものでもあります。

try/catchブロックをそのファイルを書き込むことができる領域の周りに追加することをお勧めします。 catchブロックでは、完全なファイルパスを追加し、新しい例外をスロー:

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); 
} 

ポストどんなのInnerExceptionやスタックトレースなど、あなたが受け取る完全な例外です。

また、ファイルコピーがbatch.csvに書き込むことができるかどうかを確認したい場合もあります。

+0

私は完全なファイルパスを簡潔にするために削除しました。そして、はい、ファイルにアクセスしていた唯一のプロセスは私のプロセスでした。これは単一のスレッドを使用しています。 – zonkflut

+0

あなたはスレッドを1つしか持たないかもしれませんが、スタックを持っていて、メソッドがオーバーロードされています。私の提案を試して、スタックトレースを見てください。 –

+0

私はほとんどアイデアから外れていますが、CreateFileNameがオーバーライドされたPrintDocumentメソッドで使用されているのと同じファイル名を作成する可能性はありますか?また、http://technet.microsoft.com/en-us/sysinternals/default.aspxからProcess Monitorのコピーを入手し、実行時にプログラムを監視してください。 –

0

あなたが喜んでいる場合は、永続的な修正よりも多くの障害を見つけるのを助けるためにコードに次の改造を試してみましょう。

StreamWriter paramをとる印刷文書にオーバーロードを追加します。 修正base.PrintDocuments次のように

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);   
} 

を、私はそれがよりしたTextWriter他に何もしません知っているけれども、これは、常に悲観論者...私は可能犯人としてトランスフォームを見ていることでしょう...失敗した場合、それと一緒に書く。 とにかく、ライターを作って一度解決策(クイックフィックス)になれば、時間があればそれを与えて、それが動作すれば私たちに知らせてください。

関連する問題