2

...这并不是因为文件一开始就在那里。我可以证明。

背景是这样的:我们有一个生成 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 也使用这个完全相同的类来编译项目,而我以前从未遇到过这个问题。

如果有人对此有任何见解,我将非常感谢您的意见。

4

0 回答 0