...这并不是因为文件一开始就在那里。我可以证明。
背景是这样的:我们有一个生成 WORD/PDF 文档的桌面应用程序。文档由构建块组成 - 生成特定布局的小类,可以插入不同的数据源,并且动态组合在一起以构建文档。我认为这无关紧要,但为了完整起见,我使用 ASPOSE 来生成文档。
还有另一个要求:我们需要能够更改构建块的 C# 代码并使其在生产中可用,而无需发布。我们选择的解决方案是将各种构建块的代码保存在数据库中,并在应用程序启动时进行预编译:当应用程序启动时,它从数据库中获取代码,并将它们编译成单独的然后将其转储到应用程序的临时文件夹中。为了防止“文件正在被另一个进程使用”错误,我在每个程序集的名称中添加了一个日期时间戳 - 最多毫秒。我通过将日期时间戳存储在静态变量中来跟踪程序集版本。这样,如果用户刷新应用程序,或打开另一个实例,它会安全地生成一组新的 dll' s 具有唯一的名称,这在理论上 - 确保我永远不会得到“另一个进程使用的文件”异常。当应用程序的所有实例都关闭时,临时文件夹将被擦除。(而且我们没有遇到过问题)99.9% 的时间,这个解决方案都能完美运行。它快速高效,并在我们进行更改时使 dll 保持最新。但!
我们的错误处理代码将电子邮件发送到服务器,我收到“文件正在被另一个进程使用”错误!在我的错误处理代码中,我做了以下事情: 当异常被抛出时,我得到一个文件夹中已经存在的所有 Dll 的列表。我还有一个在该迭代期间生成的所有 dll 的列表。然后,所有这些信息都包含在错误消息中。这只会增加混乱:在所有情况下,错误都发生在添加(未更新)到文件夹的最后一个文件上,没有提示可能存在重复的文件名。此外,错误永远不会发生在同一个文件上。有时它是在第一个文件被生成时,其他时候它只发生在大多数文件已经创建之后。错误也很少发生,我一直无法复制它。向用户询问更多信息没有什么价值,因为他们只有在尝试生成报告时才会注意到问题,而且可能是在他们打开应用程序数小时后。此外,此过程发生在用户实际上可以在应用程序中执行任何操作之前。
我收到的错误信息是:
消息:System.Exception:编译 E_Text 时发生错误 ---> System.Exception:8 错误:行:4 - 'System' 的 using 指令先前出现在此命名空间行:5 - 'System. Collections.Generic 之前出现在此命名空间 Line: 188 - 'C####l.Framework.Reporting.ReportWriter.GeneratorCoreExtendedBase.AddContentWithTags(string, bool, C####l.Framework.Reporting.ReportWriter.FontTemplateTypes)' 是过时:'宁可使用 ContentWithTagsParameters 参数,因为这样以后添加更多参数更容易。' 第 153 行 - 变量“ex”已声明但从未使用 行:161 - 变量“ex”已声明但从未使用 行:171 - 变量“ex”已声明但从未使用 行:182 - 变量“ ex' 已声明但从未使用 行:0 - 无法写入输出文件 'c:\Users\a#####e\Documents\T###s\Temp\E_Text_201402141156300351.dll' -- '进程不能访问该文件,因为它正被另一个进程使用。'
添加到临时文件夹的文件:
添加了-C:\Users\a#####e\Documents\T###s\Temp\ExtendedGenerator_201402141156300351.dll 添加了-C:\Users\a#####e\Documents\T###s\Temp \E_Header_1_201402141156300351.dll 添加-C:\Users\a#####e\Documents\T###s\Temp\S_StyleDefault_201402141156300351.dll 添加-C:\Users\a#####e\Documents\T## #s\Temp\E_Footer_1_201402141156300351.dll 添加-C:\Users\a#####e\Documents\T###s\Temp\E_Area_Line_Chart_201402141156300351.dll 添加-C:\Users\a#####e\Documents \T###s\Temp\H_ImageHeader_201402141156300351.dll 添加-C:\Users\a#####e\Documents\T###s\Temp\S_DefaultEditableContent_201402141156300351.dll 添加-C:\Users\a#### #e\Documents\T###s\Temp\S_DefaultCoverLetter_201402141156300351.dll 添加-C:\Users\a#####e\Documents\T###s\Temp\E_Client_Address_And_Salutation_201402141156300351.dll
当前在 TEMP 文件夹中的文件:C:\Users\a#####e\Documents\T###s\Temp\ExtendedGenerator_201402141156300351.dll C:\Users\a#####e\Documents\T###s \Temp\E_Area_Line_Chart_201402141156300351.dll C:\Users\a#####e\Documents\T###s\Temp\E_Client_Address_And_Salutation_201402141156300351.dll C:\Users\a#####e\Documents\T###s \Temp\E_Footer_1_201402141156300351.dll C:\Users\a#####e\Documents\T###s\Temp\E_Header_1_201402141156300351.dll C:\Users\a#####e\Documents\T###s \Temp\E_Text_201402141156300351.dll C:\Users\a#####e\Documents\T###s\Temp\H_ImageHeader_201402141156300351.dll C:\Users\a#####e\Documents\T###s \Temp\S_DefaultCoverLetter_201402141156300351.dll C:\Users\a#####e\Documents\T###s\Temp\S_DefaultEditableContent_201402141156300351.dll C:\Users\a#####e\Documents\T###s \Temp\S_StyleDefault_201402141156300351.dll
在 C####l.Framework.Entity.ReportingCompiler.Compile(String assemblyName, String references, String code, List`1 addedFiles) --- 内部异常堆栈跟踪结束 --- 在 C####l.Framework。 Entity.ReportingCompiler.PrecompileElements(Object o, DbTransaction& transaction) at C####l.Windows.Controls.BackgroundProcess.worker_DoWork(Object sender, DoWorkEventArgs e) - 编译 E_Text 堆栈跟踪时出错:在 C####l。 Framework.Entity.ReportingCompiler.PrecompileElements(Object o, DbTransaction& transaction) 在 C####l.Windows.Controls.BackgroundProcess.worker_DoWork(Object sender, DoWorkEventArgs e) 应用程序:#####
版本: #。#。##。####
操作系统版本:Microsoft Windows NT 6.1.7601 Service Pack 1
说明:无法预编译报告元素
编译器的代码如下:应用程序启动时调用 PrecompileElements 方法,生成报告时调用 LoadAssembly 方法。使用系统;使用 System.Collections.Generic;使用 System.CodeDom.Compiler;使用 System.Text;使用 System.IO;使用 System.Runtime.Serialization.Formatters.Binary;使用 System.Reflection;使用 System.Reflection.Emit;使用 Microsoft.CSharp;使用 C####l.Framework.Configuration;使用 C####l.Framework.Mapping; 使用 System.Data.Common;使用 C####l.Framework.Exceptions;
namespace C####l.Framework.Entity
{
public class ReportingCompiler
{
#region Members
private const string _assemblyVersionFile = "CurrentAssemblyVersion.txt";
private List<Guid> _compiledGuids = new List<Guid>();
private static string _assemblyVersion = "";
private static bool _assembliesCompilationStarted = false;
private static bool _isAssembliesCompiled = false;
private static bool _assembliesCompilationFailed = false;
private static string _assemblyLocation = "";
#endregion
#region Properties
public static bool IsAssembliesCompiled
{
get { return _isAssembliesCompiled; }
set { _isAssembliesCompiled = value; }
}
public static bool AssembliesCompilationFailed
{
get { return _assembliesCompilationFailed; }
set { _assembliesCompilationFailed = value; }
}
public static bool AssembliesCompilationStarted
{
get { return _assembliesCompilationStarted; }
}
/// <summary>
/// When set to an empty string, the value will default to the temporary folder.
/// </summary>
public static string AssemblyLocation
{
get
{
if (_assemblyLocation.Length == 0)
{
return GlobalSettings.TemporaryFolder;
}
else
{
return _assemblyLocation;
}
}
set { _assemblyLocation = value; }
}
#endregion
#region Methods
#region LoadAssembly
public static Assembly LoadAssembly(string className)
{
Assembly assembly = null;
if (!AssembliesCompilationStarted)
{
throw new FrameworkException("Cannot generate a report because the compilation of the reporting assemblies was never initiated.", false);
}
if (AssembliesCompilationFailed)
{
throw new FrameworkException("Unable to generate report because the reporting assemblies failed to compile. Please restart the application, and report the issue if the problem persist.", false);
}
else if (!IsAssembliesCompiled)
{
throw new FrameworkException("Reporting assemblies are still compiling. Please wait a few seconds, and try again.", false);
}
else
{
try
{
assembly = Assembly.LoadFile(GetFilePath(className));
}
catch (Exception ex)
{
List<string> existingFiles = GetExistingFiles();
string files = "\r\n\r\nFILES CURRENTLY IN TEMP FOLDER:\r\n";
for (int i = 0; i < existingFiles.Count; i++)
{
files = files + existingFiles[i] + "\r\n";
}
Exception ex2 = new Exception(files, ex);
throw new Exception("An error occured while trying to retrieve assembly \"" + GetFilePath(className) + "\"", ex2);
}
}
return assembly;
}
#endregion
#region PrecompileElements
public object PrecompileElements(object o, ref DbTransaction transaction)
{
_assembliesCompilationStarted = true;
_assembliesCompilationFailed = false;
_isAssembliesCompiled = false;
try
{
GlobalSettings.CheckAsposeLicense();
CreateVersionNumber();
ReportingExtendedGeneratorCoreCollection generators = (new ReportingExtendedGeneratorCoreMapper()).List();
ReportingDynamicClassDefinitionCollection elements = (new ReportingDynamicClassDefinitionMapper()).List();
string filePath = AssemblyLocation;
List<string> addedFiles = new List<string>();
for (int i = 0; i < generators.Count; i++)
{
Compile(generators[i].CSharpClassName, generators[i].CSharpReferences, generators[i].CSharpCode, addedFiles);
}
for (int i = 0; i < elements.Count; i++)
{
_isAssembliesCompiled = false;
Compile(elements[i].CSharpClassName, elements[i].CSharpReferences, elements[i].CSharpCode, addedFiles);
}
_isAssembliesCompiled = true;
return _isAssembliesCompiled;
}
catch (Exception ex)
{
_assembliesCompilationFailed = true;
throw ex;
}
}
#endregion
#endregion
#region Private methods
#region CreateVersionNumber
private void CreateVersionNumber()
{
_assemblyVersion = DateTime.Now.ToString("yyyyMMdd_HHmmss_ffff_");
StringBuilder sb = new StringBuilder(AssemblyLocation);
sb.Append(_assemblyVersionFile);
File.WriteAllText(sb.ToString(), _assemblyVersion);
}
#endregion
#region GetExistingFiles
private static List<string> GetExistingFiles()
{
List<string> currentFiles = null;
try
{
currentFiles = new List<string>(Directory.GetFiles(AssemblyLocation, "*.dll"));
}
catch (Exception ex)
{
currentFiles = new List<string>();
currentFiles.Add("Could not obtain list of existing files for the following reason: " + ex.Message);
}
return currentFiles;
}
#endregion
#region Compile
private string Compile(string assemblyName, string references, string code, List<string> addedFiles)
{
try
{
IDictionary<string, string> providerOptions = new Dictionary<string, string>();
providerOptions["CompilerVersion"] = "v3.5";
CodeDomProvider provider = new CSharpCodeProvider(providerOptions);
// ICodeCompiler compiler = new CSharpCodeProvider(providerOptions).CreateCompiler();
CompilerParameters compilerParameters = new CompilerParameters();
string[] referencedAssemblies = references.Split(';');
for (int i = 0; i < referencedAssemblies.Length; i++)
{
compilerParameters.ReferencedAssemblies.Add(referencedAssemblies[i]);
}
compilerParameters.GenerateInMemory = false; //true;
compilerParameters.OutputAssembly = GetFilePath(assemblyName);
CompilerResults compilerResults = provider.CompileAssemblyFromSource(compilerParameters, code);
if (compilerResults.Errors.HasErrors)
{
string lcErrorMsg = "";
// *** Create Error String
lcErrorMsg = compilerResults.Errors.Count.ToString() + " Errors:";
for (int x = 0; x < compilerResults.Errors.Count; x++)
lcErrorMsg = lcErrorMsg + "\r\nLine: " + compilerResults.Errors[x].Line.ToString() + " - " +
compilerResults.Errors[x].ErrorText;
lcErrorMsg = lcErrorMsg + "\r\n\r\nFILES ADDED TO THE TEMP FOLDER:\r\n";
for (int i = 0; i < addedFiles.Count; i++)
{
lcErrorMsg = lcErrorMsg + "added - " + addedFiles[i] + "\r\n";
}
List<string> existingFiles = GetExistingFiles();
lcErrorMsg = lcErrorMsg + "\r\n\r\nFILES CURRENTLY IN TEMP FOLDER:\r\n";
for (int i = 0; i < existingFiles.Count; i++)
{
lcErrorMsg = lcErrorMsg + existingFiles[i] + "\r\n";
}
throw new Exception(lcErrorMsg);
//MessageBox.Show(lcErrorMsg + "\r\n\r\n" + lcCode, "Compiler Demo", MessageBoxButtons.OK, MessageBoxIcon.Error);
//return;
}
else
{
addedFiles.Add(GetFilePath(assemblyName));
}
return compilerResults.PathToAssembly;//CompiledAssembly;
}
catch (Exception ex)
{
throw new Exception("An error occured while compiling " + assemblyName, ex);
}
}
#endregion
#region GetFilePath
private static string GetFilePath(string className)
{
StringBuilder sb = new StringBuilder(AssemblyLocation);
sb.Append(_assemblyVersion);
sb.Append(className);
sb.Append(".dll");
return sb.ToString();
}
#endregion
#endregion
}
}
到目前为止,我的结论是异常是从 CSharpCodeProvider 类中引发的,这几乎限制了我可以做的事情。几乎就好像它创建了文件,然后 - 当它想要写入它时,它会导致错误。但这很奇怪,因为 - 据我了解 - dotNet 也使用这个完全相同的类来编译项目,而我以前从未遇到过这个问题。
如果有人对此有任何见解,我将非常感谢您的意见。