2009-06-04 71 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其他好吧,写下来。 无论如何,如果你有时间,给它一个镜头,一旦创建作家可能是解决方案(快速修复),让我们知道它是否有效。

相关问题