给定了WebService地址和调用接口后动态的生成DLL,第二次再请求时会先判断DLL是否存在,以提高执行效率
核心代码下:
/// <summary> /// 动态生成WebService服务客户端 /// </summary> /// <param name="serviceProvider">WebService服务的提供者</param> /// <param name="url">WebService具体地址</param> /// <param name="serviceName">需要调用的类名</param> /// <param name="actionName">需要请求的方法</param> /// <param name="args">方法参数列表</param> /// <returns></returns> public static object InvokeWebService(string serviceProvider, string url, string serviceName, string actionName, object[] args) { if (string.IsNullOrEmpty(url)) { throw new ArgumentNullException("url"); } if (string.IsNullOrEmpty(serviceName)) { serviceName = GetWsClassName(url); } var shortUrl = BuildShortUrl(url); var nameSpace = string.Format("{0}_{1}_{2}", serviceProvider, serviceName, shortUrl); var binDir = HttpRuntime.AppDomainId == null ? AppDomain.CurrentDomain.BaseDirectory : HttpRuntime.BinDirectory; var asmFullPath = System.IO.Path.Combine(binDir, string.Format("{0}.dll", nameSpace)); #region if (!File.Exists(asmFullPath)) { if (!url.ToLower().EndsWith("?wsdl")) { url += "?wsdl"; } //获取WSDL WebClient webClient = new WebClient(); try { #region 动态生成DLL Stream wsdlStream = webClient.OpenRead(url); //服务描述 ServiceDescription serviceDesc = ServiceDescription.Read(wsdlStream); //生成客户端代理类 ServiceDescriptionImporter clientBuilder = new ServiceDescriptionImporter() { CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateNewAsync | System.Xml.Serialization.CodeGenerationOptions.GenerateProperties, ProtocolName = "Soap", Style = ServiceDescriptionImportStyle.Client }; clientBuilder.AddServiceDescription(serviceDesc, null, null); //生成客户端代理类代码 CodeNamespace ns = new CodeNamespace(nameSpace); CodeCompileUnit ccu = new CodeCompileUnit(); ccu.Namespaces.Add(ns); //按照clientBuilder的配置来生成代码 clientBuilder.Import(ns, ccu); //C#代码容器 var sourceCodeProvider = new CSharpCodeProvider(); //设定编译参数 var comParams = new CompilerParameters(); comParams.GenerateExecutable = false;//如果此属性的值为 false,则生成 DLL comParams.GenerateInMemory = false; comParams.OutputAssembly = asmFullPath; // 可以指定你所需的任何文件名。 comParams.ReferencedAssemblies.Add("System.dll"); comParams.ReferencedAssemblies.Add("System.XML.dll"); comParams.ReferencedAssemblies.Add("System.Web.Services.dll"); comParams.ReferencedAssemblies.Add("System.Data.dll"); comParams.CompilerOptions = "/optimize"; //编译 var comResult = sourceCodeProvider.CompileAssemblyFromDom(comParams, ccu); if (comResult.Errors.HasErrors) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (System.CodeDom.Compiler.CompilerError ce in comResult.Errors) { sb.Append(ce.ToString()); sb.Append(System.Environment.NewLine); } throw new Exception(sb.ToString()); } #endregion } catch (Exception ex) { } finally { webClient.Dispose(); } } #endregion //生成代理实例,并调用方法 var asm = System.Reflection.Assembly.LoadFile(asmFullPath); Type t = asm.GetType(nameSpace + "." + serviceName, true, true); object obj = Activator.CreateInstance(t); System.Reflection.MethodInfo mi = t.GetMethod(actionName); return mi.Invoke(obj, args); }
辅助代码:简化WebService地址,类似生成短链的功能
// Hash an input string and return the hash as // a 32 character hexadecimal string. static string getMd5Hash(string input) { // Create a new instance of the MD5CryptoServiceProvider object. System.Security.Cryptography.MD5 md5Hasher = System.Security.Cryptography.MD5.Create(); // Convert the input string to a byte array and compute the hash. byte[] data = md5Hasher.ComputeHash(Encoding.Default.GetBytes(input)); // Create a new Stringbuilder to collect the bytes // and create a string. StringBuilder sBuilder = new StringBuilder(); // Loop through each byte of the hashed data // and format each one as a hexadecimal string. for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("x2")); } // Return the hexadecimal string. return sBuilder.ToString(); } static string BuildShortUrl(string url) { // 可以自定义生成 MD5 加密字符传前的混合 KEY string key = "fF{#E@#:RJP(#!$"; // 要使用生成 URL 的字符 string[] chars = new string[] { "a" , "b" , "c" , "d" , "e" , "f" , "g" , "h" , "i" , "j" , "k" , "l" , "m" , "n" , "o" , "p" , "q" , "r" , "s" , "t" , "u" , "v" , "w" , "x" , "y" , "z" , "0" , "1" , "2" , "3" , "4" , "5" , "6" , "7" , "8" , "9" , "A" , "B" , "C" , "D" , "E" , "F" , "G" , "H" , "I" , "J" , "K" , "L" , "M" , "N" , "O" , "P" , "Q" , "R" , "S" , "T" , "U" , "V" , "W" , "X" , "Y" , "Z"}; // 对传入网址进行 MD5 加密 string hex = getMd5Hash(url + key); string[] resUrl = new string[4]; for (int i = 0; i < 4; i++) { // 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算 string sTempSubString = hex.Substring(i * 8, 8); // 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用 long ,则会越界 long lHexLong = 0x3FFFFFFF & long.Parse(sTempSubString, System.Globalization.NumberStyles.AllowHexSpecifier); string outChars = ""; for (int j = 0; j < 2; j++) { // 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引 long index = 0x0000003D & lHexLong; // 把取得的字符相加 outChars += chars[(int)index]; // 每次循环按位右移 7 位 lHexLong = lHexLong >> 7; } // 把字符串存入对应索引的输出数组 resUrl[i] = outChars; } return string.Join("", resUrl); }
测试结果: