动态生成WebService的客户端

给定了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);
        }
View Code

辅助代码:简化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);
        }
View Code

测试结果:

原文地址:https://www.cnblogs.com/smlheart/p/3591916.html