C#动态调用WebService

本文介绍了一种在C#中动态调用WebService的方法,包括通过URL获取WSDL文件、生成代理类并调用服务的方法。同时讨论了如何提高调用效率及在不同情况下可能出现的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【整理,内容来源于网络及本人实践】

        前面整理了一篇关于VS2008实现简单的WebService的文章,但里面的调用是静态的,当服务地址更改后,需要重新编译程序,对于很多应用来说是不现实的。本文介绍一下C#动态调用WebService的方法。

1.实现

       写个叫WebServiceHelper的类

namespace webhelp
{
    using System;
    using System.Net;
    using System.IO;
    using System.CodeDom;
    using Microsoft.CSharp;
    using System.CodeDom.Compiler;
    using System.Web.Services.Description;
    using System.Web.Services.Protocols;
    public class WebServiceHelper
    {
        #region InvokeWebService

        public static object InvokeAndCallWebService(string url, string methodname, object[] args)
        {
            return WebServiceHelper.InvokeAndCallWebService(url, null, methodname, args);
        }
        public static object InvokeAndCallWebService(string url,/* string @namespace, */string classname, string methodname, object[] args)
        {
            string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
            if ((classname == null) || (classname == ""))
            {
                classname = WebServiceHelper.GetWsClassName(url);
            }

            try
            {
                //获取WSDL   
                WebClient webClient = new WebClient();
                Stream stream = webClient.OpenRead(url + "?WSDL");
                ServiceDescription description = ServiceDescription.Read(stream);
                ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
                descriptionImporter.AddServiceDescription(description, "", "");
                CodeNamespace codeNamespace = new CodeNamespace(@namespace);

                //生成客户端代理类代码   
                CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
                codeCompileUnit.Namespaces.Add(codeNamespace);
                descriptionImporter.Import(codeNamespace, codeCompileUnit);
                CSharpCodeProvider codeProvider = new CSharpCodeProvider();

                //设定编译参数   
                CompilerParameters compilerParameters = new CompilerParameters();
                compilerParameters.GenerateExecutable = false;
                compilerParameters.GenerateInMemory = true;
                compilerParameters.ReferencedAssemblies.Add("System.dll");
                compilerParameters.ReferencedAssemblies.Add("System.XML.dll");
                compilerParameters.ReferencedAssemblies.Add("System.Web.Services.dll");
                compilerParameters.ReferencedAssemblies.Add("System.Data.dll");

                //编译代理类   
                CompilerResults codeResult = codeProvider.CompileAssemblyFromDom(compilerParameters, codeCompileUnit);
                if (true == codeResult.Errors.HasErrors)
                {
                    System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
                    foreach (System.CodeDom.Compiler.CompilerError compileError in codeResult.Errors)
                    {
                        stringBuilder.Append(compileError.ToString());
                        stringBuilder.Append(System.Environment.NewLine);
                    }
                    throw new Exception(stringBuilder.ToString());
                }

                //生成代理实例,并调用方法   
                System.Reflection.Assembly assembly = codeResult.CompiledAssembly;
                Type @class = assembly.GetType(@namespace + "." + classname, true, true);
                object instance = Activator.CreateInstance(@class);
                System.Reflection.MethodInfo methorInfo = @class.GetMethod(methodname);

                return methorInfo.Invoke(instance, args);

            }
            catch (Exception ex)
            {
                throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));
            }
        }

        private static string GetWsClassName(string wsUrl)
        {
            string[] parts = wsUrl.Split('/');
            string[] pps = parts[parts.Length - 1].Split('.');

            return pps[0];
        }
        #endregion
    }
}

2.注意

1.)若编译提示“命名空间“System.Web”中不存在类型或命名空间名称“Services”(是缺少程序集引用吗?)”,只需在解决方案资源管理器中添加对System.Web.Services的程序集引用即可。

2.)不建议调用

public static object InvokeAndCallWebService(string url, string methodname, object[] args)

即不指定类名,让程序由URL中分析出类名,因为不是所有服务的类名都放在URL的最后一个‘/’和‘.’中间的。

3.)@namespace不一定要取"EnterpriseServerBase.WebService.DynamicWebCalling",从我的实验中,取任意@namespace,甚至@namespace是空串都能够正常运行。

4.)网上有人说用这种方法调用Java写的Webservice时有问题,说是异常提示报的是无法获得相应空间里的类,有人说跟Java不要命名空间有关。我特意请同学帮我做一个java 的webservice(因为我不会java),实验证明这种方法并没有什么问题,我猜测报异常可能是因为没有指定类名,而是代码根据url分析类名,或者有可能是服务返回类型是一些复杂类型的值,建议返回类型最好是string或int之类的。

5.)在这种方法中url、命名空间、类名、函数名都是字符串,可以根据需要进行赋值。

3.调用

注意到上面实现的InvokeAndCallWebService是个静态函数,所以想利用WebServiceHelper调用Webservice方法如下。

            string url = "http://192.168.0.113/NM2SMARTB/INM2SmartSoap11Binding.asmx";
            string[] args = new string[1];
            args[0] = "hello";
            object result = webhelp.WebServiceHelper.InvokeAndCallWebService(url, "Nm2Smart", "getDevices", args);

4.变化

        上面的WebServiceHelper利用静态方法实现动态调用WebService,但是仍存在一个问题,就是每次函数InvokeAndCallWebService都要重新获取服务、生成代理等一系列过程,这样子的效率很低。所以可以对WebServiceHelper稍作修改,在服务没更改的的时候,只生成一次代理,可以多次调用其中的函数。大部分代码仍和上面一样。只是将服务实例化代码和调用分开,将InvokeAndCallWebService中的局部变量instance和@class变为类成员变量。如此只要声明一个WebServiceHelper对象,就可以只生成一次代理,多次调用函数,直到必要的时候再重新生成代理实例。当然此时函数已不是静态函数。
namespace webhelp
{
    using System;
    using System.Net;
    using System.IO;
    using System.CodeDom;
    using Microsoft.CSharp;
    using System.CodeDom.Compiler;
    using System.Web.Services.Description;
    using System.Web.Services.Protocols;
    public class WebServiceHelper
    {
 #region InvokeWebService
        object instance;//将原来函数内的局部变量变为类成员
        Type @class;//将原来函数内的局部变量变为类成员

        public void InvokeWebService(string url,/* string @namespace,*/ string classname)
        {
            string @namespace = "EnterpriseServerBase.WebService.DynamicWebCalling";
            if ((classname == null) || (classname == ""))
            {
                classname = WebServiceHelper.GetWsClassName(url);
            }

            try
            {
                //获取WSDL   
                WebClient webClient = new WebClient();
                Stream stream = webClient.OpenRead(url + "?WSDL");
                ServiceDescription description = ServiceDescription.Read(stream);
                ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
                descriptionImporter.AddServiceDescription(description, "", "");
                CodeNamespace codeNamespace = new CodeNamespace(@namespace);

                //生成客户端代理类代码   
                CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
                codeCompileUnit.Namespaces.Add(codeNamespace);
                descriptionImporter.Import(codeNamespace, codeCompileUnit);
                CSharpCodeProvider codeProvider = new CSharpCodeProvider();

                //设定编译参数   
                CompilerParameters compilerParameters = new CompilerParameters();
                compilerParameters.GenerateExecutable = false;
                compilerParameters.GenerateInMemory = true;
                compilerParameters.ReferencedAssemblies.Add("System.dll");
                compilerParameters.ReferencedAssemblies.Add("System.XML.dll");
                compilerParameters.ReferencedAssemblies.Add("System.Web.Services.dll");
                compilerParameters.ReferencedAssemblies.Add("System.Data.dll");

                //编译代理类   
                CompilerResults codeResult = codeProvider.CompileAssemblyFromDom(compilerParameters, codeCompileUnit);
                if (true == codeResult.Errors.HasErrors)
                {
                    System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();
                    foreach (System.CodeDom.Compiler.CompilerError compileError in codeResult.Errors)
                    {
                        stringBuilder.Append(compileError.ToString());
                        stringBuilder.Append(System.Environment.NewLine);
                    }
                    throw new Exception(stringBuilder.ToString());
                }

                //生成代理实例  
                System.Reflection.Assembly assembly = codeResult.CompiledAssembly;
                @class = assembly.GetType(@namespace + "." + classname, true, true);
                instance = Activator.CreateInstance(@class);
                // return instance;                
            }
            catch (Exception ex)
            {
                throw new Exception(ex.InnerException.Message, new Exception(ex.InnerException.StackTrace));
            }
        }
        public object CallService(string methodname, object[] args)//调用方法与生成代理实例分开
        {
            try
            {
                System.Reflection.MethodInfo methorInfo = @class.GetMethod(methodname);
                return methorInfo.Invoke(instance, args);
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        #endregion
    }
}

5.疑问

        作为一个新手,虽然知道动态调用的方法,但是其中一些东西还不是很了解,就像某老师说的“知其然不知其所以然”,希望能够有热心朋友帮我解答。比如@namespace是指什么的命名空间?我的想法是,这应该是指生成代理类所在的命名空间,但是如果我多次创建同一个代理,那不是在同一命名空间中存在多个同名类?却没有报错。或是我故意指定@namespace为webhelp,并在webhelp中定义一个同名的类,程序也是正常的。这里这个命名空间的作用是什么呢?即使指定命名空间为空字符串,仍是没有问题的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值