2009-06-04 48 views
0

我已經實現了一個csv文件構建器,它接受一個xml文檔對其應用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; } 
} 

我有大量的XML文檔添加到這個csv文件,並多次調用它後,它有時拋出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()方法嗎?如果這是意圖,那麼也許從最近的經驗來看,如果意圖是將每個文檔寫入同一個文件,那麼我會在using塊中添加一個.Close(),即使應該在超出範圍時調用Dispose() ,它應該關閉文件。花費沒有什麼可嘗試,並會帶你幾分鐘 – 2009-06-04 01:31:02

+0

西蒙,你應該添加,作爲一個答案.... – zonkflut 2009-06-04 02:58:00

回答

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

只有一件事可以導致這種情況。另一個過程是寫入文件。另一個過程甚至可以是你自己的。

我建議你在可寫入該文件的區域周圍添加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

如果您願意,可以嘗試下面的代碼修改代碼,以幫助您找到故障而不是永久修復。

添加一個超載來打印需要StreamWriter參數的文檔。 修改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其他好吧,寫下來。 無論如何,如果你有時間,給它一個鏡頭,一旦創建作家可能是解決方案(快速修復),讓我們知道它是否有效。