c# – ASP.NET中的动态代码
在我们的ASP.NET应用程序中,我们有一个功能,允许用C#或VB.NET代码编写脚本.这些脚本存储在数据库中,并按指定的时间间隔进行编译,执行存储在这些脚本中的代码.
只要用户编写基本的.NET代码,这就可以工作.当然,我们的客户现在要求他们能够引用自己的DLL以允许执行特定代码.这对我们来说是可以接受的,我们正在为此创建一个解决方案.但是,我们希望在任何时候都避免使用特定方案: 不允许应用程序将引用的DLL文件复制到ASP.NET端的BIN文件夹中,因为这会重新启动应用程序并且不支持/允许 我一直在玩CompilerOptions类,我注意到你可以在那里设置引用的库.从我在MSDN网站上找到的信息: >您可以使用以下命令设置库路径:CompilerOptions = 在我们的脚本中,我们还有以下机制;用户可以在其代码中定义一个References区域,该区域包含执行脚本所需的各种自定义DLL的路径.示例脚本可能如下所示: #region References /* * C:Program FilesMailBeeMailBee.Net.dll * C:Program FilesCustomAppCustom.dll * System.IO.dll /* #endregion namespace custom.script.space { class CustomScript : Script { [EntryPoint] public voic run() { // do stuff } } } 这将引用System.IO程序集和指定的两个自定义DLL.但是,对于当前的实现,我们将自定义DLL复制到GAC,然后只添加它们的名称作为编译器的引用. 是否可以禁用DLL的副本并使用这些dll的完整路径进行引用,而无需将它们复制到应用程序的GAC / bin文件夹中?是否可以使用CompilerOptions来设置libpath并让所有引用都指向这个? 我们之所以不想复制Dll并重新启动应用程序,是因为我们有多实例应用程序,单个实例上有多个客户,而且我们不能简单地重启应用程序. 我希望问题很清楚我正在努力实现的目标…… 解决方法
我正在使用的代码似乎工作正常,没有我指定特定的程序集.编译脚本并加载所有动态引用的代码如下所示:
/// <summary> /// Gets the dynamic references. /// </summary> /// <param name="source">The source.</param> /// <param name="assemblyDllPath">The assembly DLL path.</param> /// <returns></returns> private string[] GetDynamicReferences(string source,string assemblyDllPath) { var filenames = new List<string>(); const string startRegion = "#region References"; const string endRegion = "#endregion"; const string commentStart = "/*"; const string commentEnd = "*/"; const string commentLine = "//"; const string libpath = "/libpath"; var sourceReader = new StringReader(source); string currentLine; bool inReferenceRegion = false; bool inReferenceCommentRegion = false; // Loop over the lines in the script and check each line individually. while ((currentLine = sourceReader.ReadLine()) != null) { // Strip the current line of all trailing spaces. currentLine = currentLine.Trim(); // Check if we're entering the region 'References'. if (currentLine.StartsWith(startRegion)) { inReferenceRegion = true; // We're entering the region,set the flag. continue; // Skip to the next line. } // Check if we're exiting the region 'References'. If so,stop the for loop. if (currentLine.StartsWith(endRegion)) break; // If we're processing a line that's not in the 'References' region,then skip the line // as we're only interested in the lines from that region. if (!inReferenceRegion) continue; // Check if we're entering the comments section,because the entire region is actually // a big comment block,starting with /* if (currentLine.StartsWith(commentStart)) { inReferenceCommentRegion = true; // We're entering the comment block. continue; // Skip to the next line. } // Check if we're leaving the comments section,because then we're almost done parsing // the entire comment block. if (currentLine.EndsWith(commentEnd)) { inReferenceCommentRegion = false; // Leaving the comment block. continue; // Skip to the next line. } // If the line we're processing starts with a comment '//',then skip the line because it's // not to be processed anymore by us,just as if it was placed in comment in real code. // If the line contains a double slash,strip one of the slashes from it and parse the data. if (currentLine.Contains(commentLine)) { if (currentLine.StartsWith(commentLine)) continue; currentLine = currentLine.Substring(0,currentLine.IndexOf(commentLine) - 1); } // If we're dealing with a line that's not inside the reference comment section,skip it // because we're only interested in the lines inside the comment region section of the script. if (!inReferenceCommentRegion) continue; // Trim the current line of all trailing spaces,the line should represent either the fullpath // to a DLL,the librarypath option,or the relative path of a DLL. string line = currentLine.Trim(); // If the line starts with the library option,then we need to extract this information,and store it // inside the local varialbe that holds the libpath. if (line.Equals(libpath)) { string dataHomeFolder = Api2.Factory.CreateApi().Parameters.Read(343).Value; string companyName = Api2.Factory.CreateApi().Parameters.Read(113).Value; _libraryPath = Path.Combine(dataHomeFolder,companyName,"libraries"); } // If the line is not an absolute path to the referenced DLL,then we need to assume that the DLL resides // in the library path. We'll build up the full path using the library path,if the path has been set. if (!Path.IsPathRooted(line) && !string.IsNullOrEmpty(_libraryPath)) line = Path.Combine(_libraryPath,line); // If the file exists,then we'll add it as reference to the collection to be used by the compiler. // We will not copy the file however in the bin folder of the application. var fio = new FileInfo(line); if (fio.Exists && !filenames.Contains(line)) filenames.Add(line); } // Return the entire collection of libraries. return filenames.ToArray(); } 这会将我在区域块中定义的所有动态引用加载到编译器中.使用C#.NET中的Compile类,我可以从脚本编译我的源代码并链接到外部DLLS. 此代码执行编译: /// <summary> /// <para>This function performs the compile operation and return the compiled assembly.</para> /// </summary> /// <param name="source">The source code of the script to compile.</param> /// <param name="libs">A collection of additional libraries to compile the script.</param> /// <returns>The compiled assembly.</returns> internal Assembly Compile(string source,List<string> libs) { var libraries = new List<string>(libs); CodeDomProvider codeProvider = new CSharpCodeProvider(new Dictionary<string,string> { { "CompilerVersion","v4.0" } }); var compilerParams = new CompilerParameters { CompilerOptions = "/target:library /optimize",GenerateExecutable = false,GenerateInMemory = true,IncludeDebugInformation = true,TreatWarningsAsErrors = false }; string assemblyDllPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath); // Load all the required assemblies depending on the api implementation. LoadAssemblies(compilerParams,source,assemblyDllPath,libraries); var path = Path.Combine(Path.GetTempPath(),"TF-" + Guid.NewGuid().ToString().ToUpper()); // replace resx-files from provided libraries with compatible dll's var resxs = libraries.FindAll(lb => lb.EndsWith(".resx",StringComparison.OrdinalIgnoreCase)); var tmpFiles = new List<string>(); if (resxs.Count > 0) { if (!Directory.Exists(path)) Directory.CreateDirectory(path); foreach (var resx in resxs) { // Get the resources filename var resourceFilename = Path.GetFileNameWithoutExtension(resx); var filename = Path.Combine(path,resourceFilename + ".resources"); File.Delete(filename); tmpFiles.Add(filename); // Create a ResXResourceReader for the file items.resx. Stream stream = File.Open(resx,FileMode.Open,FileAccess.Read,FileShare.Read); var rsxr = new ResXResourceReader(stream); // Create a ResXResourceReader for the file items.resources. IResourceWriter writer = new ResourceWriter(filename); // Iterate through the resources and add resources to the resource writer. IDictionary dictionary = new Dictionary<string,string>(); foreach (DictionaryEntry d in rsxr) { var k = d.Key.ToString(); var v = d.Value.ToString(); dictionary.Add(k,v); writer.AddResource(k,v); } // Close the reader. rsxr.Close(); stream.Close(); writer.Close(); compilerParams.EmbeddedResources.Add(filename); string[] errors; var provider = new CSharpCodeProvider(); // c#-code compiler var cu = StronglyTypedResourceBuilder.Create(dictionary,resourceFilename ?? string.Empty,"",provider,false,out errors); var options = new CodeGeneratorOptions { BracingStyle = "C",BlankLinesBetweenMembers = false,IndentString = "t" }; var tw = new StringWriter(); provider.GenerateCodeFromCompileUnit(cu,tw,options); var libCode = tw.ToString(); tw.Close(); if (!libraries.Contains(libCode)) libraries.Add(libCode); } libraries.RemoveAll(lb => lb.EndsWith(".resx",StringComparison.OrdinalIgnoreCase)); } // actually compile the code CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams,new List<string>(libraries) { source }.ToArray()); // remove the temporary files foreach (var file in tmpFiles) File.Delete(file); // remove the resource directory if(Directory.Exists(path)) Directory.Delete(path); if (results.Errors.HasErrors) { var sb = new StringBuilder("Compilation error :nt"); foreach (CompilerError error in results.Errors) sb.AppendLine("t" + error.ErrorText); throw new Exception(sb.ToString()); } //get a hold of the actual assembly that was generated Assembly generatedAssembly = results.CompiledAssembly; // move to some app startup place (this only needs to be set once) if (!API.Factory.IsAPIImplementationTypeSet) { API.Factory.SetAPIImplementation(Assembly.LoadFile(assemblyDllPath + "TenForce.Execution.API.Implementation.dll").GetType("TenForce.Execution.API.Implementation.API")); } // Set the implementation type for the API2 as well. This should only be set once. if (!Api2.Factory.ImplementationSet) { Api2.Factory.SetImplementation(Assembly.LoadFile(assemblyDllPath + "TenForce.Execution.Api2.Implementation.dll").GetType("TenForce.Execution.Api2.Implementation.Api")); } return generatedAssembly; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |