解决WebService 第一次慢的问题 在wince下
正好做一个wince设备的项目,以前也做过类似的,但是应用属于轻量级的, 因此对于web service慢的问题没有太多关注。这次也同样发现了这个问题, 调式几次连Hello,World 都很慢,简直无法容忍。于是在网上搜索此问题的解决方法。 问题的原因很简单,就是因为在第一次连结web service时应用程序动态编译生成序列化程序集导致的, 在http://support.microsoft.com/kb/872800/zh-cn或http://support.microsoft.com/kb/872800/en-us上 有详细的解释,并附有PreGen.exe工具的源码文章上的建议是使用PreGen.exe工具直接生成序列化程序集, 在程序中直接加载,解决每次动态生成从而加快web service的速度。但是文章中 使用[System.Xml.Serialization.XmlSerializerAssemblyAttribute(CodeBase="< DLL 名")] 因为dotnetCF没有System.Xml.Serialization.XmlSerializerAssemblyAttribute属性。 在网上查了大多,也没有找到在wince环境下如何加载的方法。 又仔细查看了英文的文档,有一条命令引起的我的注意那就是LoadFrom 至此在wince下调用web service慢的问题完全解决 ? using System; using System.Collections.Generic; using System.Windows.Forms; namespace TestWeb { using System.Reflection; static class Program { /// <summary> /// 应用程序的主入口点。 /// </summary> [MTAThread] static void Main() { Assembly.LoadFrom("debugTestWeb.XmlSerializers.dll"); Application.Run(new Main()); } } } 另外给大家附上另一个生成序列化程序集的方法(仅限PC端,此方法程序自动加载序列化程序集) ? ? ? PreGen.cs源代码,简单版 namespace PreGenNS { using System; using System.Collections; using System.IO; using System.Reflection; using System.Xml.Serialization; using System.Text; using System.Globalization; using System.Web.Services.Protocols; using System.Threading; using System.CodeDom.Compiler; public class Pregen { public static int Main(string[] args) { if (args.Length != 1) { Console.WriteLine("usage: "); Console.WriteLine(" pregen assembly"); return 1; } Pregen pregen = new Pregen(); return pregen.Run(args[0]); } int Run(string assemblyName) { try { GenerateAssembly(assemblyName); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } Error(e,"Error: "); return 1; } return 0; } void GenerateAssembly(string assemblyName) { Assembly assembly = LoadAssembly(assemblyName,true); Type[] types = assembly.GetTypes(); ArrayList mappings = new ArrayList(); ArrayList importedTypes = new ArrayList(); XmlReflectionImporter importer = new XmlReflectionImporter(); for (int i = 0; i < types.Length; i++) { Type type = types[i]; if (HttpWebClientProtocol.GenerateXmlMappings(type,mappings)) { importedTypes.Add(type); } } if (importedTypes.Count > 0) { Type[] serializableTypes = (Type[])importedTypes.ToArray(typeof(Type)); XmlMapping[] allMappings = (XmlMapping[])mappings.ToArray(typeof(XmlMapping)); bool gac = assembly.GlobalAssemblyCache; string codePath = gac ? Environment.CurrentDirectory : Path.GetDirectoryName(assembly.Location); string serializerName = assembly.GetName().Name + ".XmlSerializers" ; string location = Path.Combine(codePath,serializerName + ".dll"); CompilerParameters parameters = new CompilerParameters(); parameters.TempFiles = new TempFileCollection(codePath); parameters.GenerateInMemory = false; parameters.IncludeDebugInformation = false; parameters.TempFiles = new TempFileCollection(codePath,true); Assembly serializer = XmlSerializer.GenerateSerializer(serializableTypes,allMappings,parameters); if (serializer == null) { Console.Out.WriteLine("Failed pregenerate serializer for '{0}'",assembly.Location); } else { AssemblyName name = serializer.GetName(); Console.Out.WriteLine("Serialization Assembly Name: {0}",name.ToString()); Console.Out.WriteLine("Generated serialization assembly for assembly {0} --> '{1}'.",assembly.Location,location); } } else { Console.Out.WriteLine("Assembly '{0}' does not contain any serializable types.",assembly.Location); } } static Assembly LoadAssembly(string assemblyName,bool throwOnFail) { Assembly assembly = null; string path = Path.GetFullPath(assemblyName).ToLower(CultureInfo.InvariantCulture); if (File.Exists(path)) { assembly = Assembly.LoadFrom(path); } else { try { assembly = Assembly.Load(assemblyName); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } Error(e,"Error: "); } if (assembly == null) { string justName = Path.GetFileNameWithoutExtension(assemblyName); try { assembly = Assembly.Load(justName); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } Error(e,"Error: "); } } } if (assembly == null) { if (throwOnFail) throw new InvalidOperationException("Cannot load assembly " + assemblyName); return null; } return assembly; } static void Error(Exception e,string prefix) { Console.Error.WriteLine(prefix + e.Message); if (e.InnerException != null) { Error(e.InnerException," - "); } } static void Warning(Exception e) { Console.Out.WriteLine(" - " + e.Message); if (e.InnerException != null) { Warning(e.InnerException); } } } }
namespace PreGenNS { using System; using System.Collections; using System.IO; using System.Reflection; using System.Xml.Serialization; using System.Text; using System.Globalization; using System.Web.Services.Protocols; using System.Threading; using System.CodeDom.Compiler; using System.Diagnostics; using System.Text.RegularExpressions; public class Pregen { private static bool _verbose = true; // We use this as the standard suffix on all the serializer DLLs. // It must match the short name of the proxy assembly class with const string SerializerSuffix = ".Serializer.dll"; /// Key in the app config file with path of AssemblyInfo file. private const string AssemblyInfoAppKey = "AssemblyInfoFile"; //private const string CSCCmd = "csc.exe"; // Obtain the full path for the compiler,just in case the path is not set correctly private readonly string CSCCmd = System.Runtime.InteropServices.RuntimeEnvironment.RuntimeDirectory() + "csc.exe"; public static int Main(string[] args) { // Are we in a recursive call? //TODO: could really use a single value -- use filesToDelete... if (AppDomain.CurrentDomain.GetData(CallingAppDomainKey) == null) { return RunSlave(args); } string dllName = ""; bool invalidUsage = false; if(args.Length == 1) { dllName = args[0]; } else if(args.Length == 2) { if(!(args[0] == "/Q" || args[0] == "/q")) { invalidUsage = true; } else { dllName = args[1]; _verbose = false; } } else { invalidUsage = true; } if (invalidUsage) { Console.WriteLine("usage: "); Console.WriteLine(" pregen [/Q] assembly"); return 1; } // Update Private path setting in current application domain if (updatePrivatePath() != 0) { return 1; } Pregen pregen = new Pregen(); int rc = pregen.Run(dllName); //Console.Read(); return rc; } // Reads private path settings from config file and updates appdomain. This permits configurable probing that is needed for preserialization. static int updatePrivatePath() { string defaultPrivatePath = System.Configuration.ConfigurationSettings.AppSettings["PregenDefaultPrivatePath"]; string dynamicPrivatePath = System.Configuration.ConfigurationSettings.AppSettings["PregenDynamicPrivatePath"]; string env_PREGEN_VALUES = Environment.GetEnvironmentVariable("PREGEN_VALUES"); string [] replacementBlocks,temp; if (_verbose) Console.WriteLine("Read PREGEN_VALUES Env Variable,Value = " + env_PREGEN_VALUES); //process the dynamic path if the environment variable PREGEN_VALUES is present if (env_PREGEN_VALUES == null || env_PREGEN_VALUES == "") { if (defaultPrivatePath != null && defaultPrivatePath != "") { AppDomain.CurrentDomain.AppendPrivatePath(defaultPrivatePath); if (_verbose) Console.WriteLine("Appended private path with: " + defaultPrivatePath); } } else { if (dynamicPrivatePath != null && dynamicPrivatePath != "") { //do substitutions in dynamic path replacementBlocks = env_PREGEN_VALUES.ToUpper().Split(";".ToCharArray()); dynamicPrivatePath = dynamicPrivatePath.ToUpper(); for(int i = 0; i < replacementBlocks.Length; i++) { temp = replacementBlocks[i].Split("=".ToCharArray()); if(temp.Length != 2) { Console.Error.WriteLine("Invalid Environment Variable format - PREGEN_VALUES"); return 1; } dynamicPrivatePath = dynamicPrivatePath.Replace(temp[0],temp[1]); } AppDomain.CurrentDomain.AppendPrivatePath(dynamicPrivatePath); if (_verbose ) Console.WriteLine("Appended private path with: " + dynamicPrivatePath); } } return 0; } int Run(string assemblyName) { try { GenerateAssembly(assemblyName); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } Error(e,"Error processing " + assemblyName + ": "); return 1; } return 0; } // Generates the serializer assembly for the proxy assembly specified void GenerateAssembly(string assemblyName) { Assembly assembly = LoadAssembly(assemblyName,true); Type[] types = assembly.GetTypes(); ArrayList mappings = new ArrayList(); ArrayList importedTypes = new ArrayList(); XmlReflectionImporter importer = new XmlReflectionImporter(); //Obtain the imported serializable types for (int i = 0; i < types.Length; i++) { Type type = types[i]; if (HttpWebClientProtocol.GenerateXmlMappings(type,mappings)) { importedTypes.Add(type); } } if (importedTypes.Count <= 0) { Console.Out.WriteLine("Assembly '{0}' does not contain any serializable types.",assembly.Location); return; } { Type[] serializableTypes = (Type[])importedTypes.ToArray(typeof(Type)); XmlMapping[] allMappings = (XmlMapping[])mappings.ToArray(typeof(XmlMapping)); bool wasError = false; bool gac = assembly.GlobalAssemblyCache; string codePath = gac ? Environment.CurrentDirectory : Path.GetDirectoryName(assembly.Location); //adjust compiler params CompilerParameters parameters = new CompilerParameters(); parameters.GenerateInMemory = false; parameters.IncludeDebugInformation = false; parameters.TempFiles = new TempFileCollection(codePath,true); //generate the serializer Assembly serializer = XmlSerializer.GenerateSerializer(serializableTypes,parameters); if (serializer == null) { Console.Out.WriteLine("Failed pregenerate serializer for '{0}'",assembly.Location); wasError = true; } else { serializer = null; } // Determine whether there is an assemblyInfoFile in the config file. string assemblyInfoFile = System.Configuration.ConfigurationSettings.AppSettings[AssemblyInfoAppKey]; if (assemblyInfoFile != null) { if (! File.Exists(assemblyInfoFile)) { Console.WriteLine("ERROR: AssemblyInfo file: {0} does not exist.",assemblyInfoFile); wasError = true; } } if (!wasError) { // Recompile the Serializer,same options,except to include the assemblyInfo file and // adjust the output name. // We have to find // 1. a .cs file (the serializer source) // 2. a .cmdline file (the compiler options used) // among the temp files from the first compile. string csFile = null; string cmdlineFile = null; foreach (string curFile in parameters.TempFiles ) { string fileNameLC = curFile.ToLower(); if (fileNameLC.EndsWith(".cs") ) { csFile = curFile; } else if (fileNameLC.EndsWith(".cmdline")) { cmdlineFile = curFile; } //Do not care about the other files... } if (csFile == null || cmdlineFile == null) { Console.WriteLine("Error: needed to rebuild,but cannot find either .cs or .cmdline filen"); DeleteTempFiles(parameters); return; } // So now we have found the file and the cmdline args. We only need run the compiled application after // adjusting the parameters to include the AssemblyInfo file and to change the output. // Typical calling options to csc for this sequence are: // csc /noconfig @xxx.cmdline // we'll change this to expand the contents of the cmdline file // build the right name for the target serializer. //TODO: we should be able to read the attribute from the proxy DLL and match our output // to that name. string serializerName = Path.GetDirectoryName(assembly.Location) + @"" + assembly.GetName().Name + SerializerSuffix; string cmdLine = AdjustCmdLine(cmdlineFile,assemblyInfoFile,serializerName); ProcessStartInfo ps = new ProcessStartInfo(CSCCmd,cmdLine); ps.WindowStyle = ProcessWindowStyle.Hidden; Process p = Process.Start(ps); p.WaitForExit(); int rc = p.ExitCode; if (rc > 0) { //TODO: put useful handling here... Console.WriteLine("ERROR: Compiler problem,rc = {0}",rc); wasError = true; } //TODO: Cannot ditch temp assembly because the assembly is now loaded. DeleteTempFiles(parameters); if (!wasError) { Console.Out.WriteLine("Generated Serialization Assembly Name: {0}",serializerName); } Console.Out.WriteLine("Done"); } } } // Delete temporary files from a CompilerParameters list. private void DeleteTempFiles(CompilerParameters cp) { ArrayList unDeletedFiles = new ArrayList(10); foreach(string fileName in cp.TempFiles) { try { File.Delete(fileName); } catch(Exception) { unDeletedFiles.Add(fileName); //Console.WriteLine("Warning: Unable to delete temp file: {0},exception={1}("{2}")",// Path.GetFileName(fileName),e.GetType().FullName,e.Message); } } if (unDeletedFiles.Count > 0) { // put the list into the calling appDomain's environment for later deletion string[] files = new string[unDeletedFiles.Count]; unDeletedFiles.CopyTo(files); //TODO: should really be concatenating to any existing value -- maybe leave as an ArrayList? AppDomain.CurrentDomain.SetData(FilesToDeleteKey,files); } } /// Rebuild a commandline for csc,adding assemblyInfoFile to the end of the line and adjusting the /// output file name as specified. private string AdjustCmdLine(string cmdlineFile,string assemblyInfoFile,string outputFile) { // Obtain the text from the @ response file that is used by the Framework Serialization builder StreamReader file = File.OpenText(cmdlineFile); string cmdLine = file.ReadToEnd(); file.Close(); // add the assemblyInfo file at the end of the command if it was specified if (assemblyInfoFile != null) cmdLine = String.Format(@"/noconfig {0} ""{1}""",cmdLine,assemblyInfoFile); // replace the /OUT option with our value. Regex re = new Regex(@"/OUT:""[^""]+.",RegexOptions.IgnoreCase); cmdLine = re.Replace(cmdLine,@"/OUT:""" + outputFile + @""""); return cmdLine; } static Assembly LoadAssembly(string assemblyName,bool throwOnFail) { Assembly assembly = null; string path = Path.GetFullPath(assemblyName).ToLower(CultureInfo.InvariantCulture); if (File.Exists(path)) { assembly = Assembly.LoadFrom(path); } else { try { assembly = Assembly.Load(assemblyName); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } Error(e,"Error: "); } if (assembly == null) { string justName = Path.GetFileNameWithoutExtension(assemblyName); try { assembly = Assembly.Load(justName); } catch (Exception e) { if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) { throw; } Error(e,"Error: "); } } } if (assembly == null) { if (throwOnFail) throw new InvalidOperationException("Cannot load assembly " + assemblyName); return null; } return assembly; } static void Error(Exception e,string prefix) { Console.Error.WriteLine(prefix + e.Message); if (e.InnerException != null) { Error(e.InnerException," - "); } } static void Warning(Exception e) { Console.Out.WriteLine(" - " + e.Message); if (e.InnerException != null) { Warning(e.InnerException); } } private static AppDomain DuplicateAppDomain(AppDomain template,string newName) { AppDomain res = AppDomain.CreateDomain(newName,template.Evidence,template.SetupInformation); return res; } // keys in AppDomain properties private const string CallingAppDomainKey = "__Calling_AppDomain__"; private const string FilesToDeleteKey = "__Files_To_Delete__"; // Called from Main to set up and run the second copy of the program. // args: command-line arguments for second execution private static int RunSlave(string[] args) { // Start a copy of this program in another application domain AppDomain ad = DuplicateAppDomain(AppDomain.CurrentDomain,"serializerAD"); Assembly ca = Assembly.GetExecutingAssembly(); // set a marker so target domain knows that it is the subordinate. ad.SetData(CallingAppDomainKey,AppDomain.CurrentDomain.FriendlyName); ad.SetData(FilesToDeleteKey,new string[0]); int rc = ad.ExecuteAssembly(ca.Location,ca.Evidence,args); // Now delete any files string[] fileList = (string[])ad.GetData(FilesToDeleteKey); AppDomain.Unload(ad); if (fileList != null) { foreach(string fileName in fileList) { try { File.Delete(fileName); } catch(Exception e) { Console.WriteLine("Warning: Unable to delete temp file: {0},Path.GetFileName(fileName),e.Message); } } } return rc; } } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |