Skip to main content
 首页 » 编程设计

c#之在不添加 WebReference 的情况下调用 Webservice之具有复杂类型

2024年01月16日21lidabo

我正在使用 This Site 的代码动态调用网络服务。

[SecurityPermissionAttribute(SecurityAction.Demand, Unrestricted = true)] 
    public static object CallWebService(string webServiceAsmxUrl, string serviceName, string methodName, object[] args) 
    { 
        System.Net.WebClient client = new System.Net.WebClient(); 
        //-Connect To the web service 
        using (System.IO.Stream stream = client.OpenRead(webServiceAsmxUrl + "?wsdl")) 
        { 
            //--Now read the WSDL file describing a service. 
            ServiceDescription description = ServiceDescription.Read(stream); 
            ///// LOAD THE DOM ///////// 
            //--Initialize a service description importer. 
            ServiceDescriptionImporter importer = new ServiceDescriptionImporter(); 
            importer.ProtocolName = "Soap12"; // Use SOAP 1.2. 
            importer.AddServiceDescription(description, null, null); 
            //--Generate a proxy client. importer.Style = ServiceDescriptionImportStyle.Client; 
            //--Generate properties to represent primitive values. 
            importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties; 
            //--Initialize a Code-DOM tree into which we will import the service. 
            CodeNamespace nmspace = new CodeNamespace(); 
            CodeCompileUnit unit1 = new CodeCompileUnit(); 
            unit1.Namespaces.Add(nmspace); 
            //--Import the service into the Code-DOM tree. This creates proxy code 
            //--that uses the service. 
            ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1); 
            if (warning == 0) //--If zero then we are good to go 
            { 
                //--Generate the proxy code  
                CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp"); 
                //--Compile the assembly proxy with the appropriate references 
                string[] assemblyReferences = new string[5] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" }; 
                CompilerParameters parms = new CompilerParameters(assemblyReferences); 
                CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1); 
                //-Check For Errors 
                if (results.Errors.Count > 0) 
                { 
                    StringBuilder sb = new StringBuilder(); 
                    foreach (CompilerError oops in results.Errors) 
                    { 
                        sb.AppendLine("========Compiler error============"); 
                        sb.AppendLine(oops.ErrorText); 
                    } 
                    throw new System.ApplicationException("Compile Error Occured calling webservice. " + sb.ToString()); 
                } 
                //--Finally, Invoke the web service method  
                Type foundType = null; 
                Type[] types = results.CompiledAssembly.GetTypes(); 
                foreach (Type type in types) 
                { 
                    if (type.BaseType == typeof(System.Web.Services.Protocols.SoapHttpClientProtocol)) 
                    { 
                        Console.WriteLine(type.ToString()); 
                        foundType = type; 
                    } 
                } 
 
                object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString()); 
                MethodInfo mi = wsvcClass.GetType().GetMethod(methodName); 
                return mi.Invoke(wsvcClass, args); 
            } 
            else 
            { 
                return null; 
            } 
        } 
    } 

当我使用内置类型时,这很好用,但是对于我自己的类,我得到了这个:
Event Type: Error 
Event Source:   TDX Queue Service 
Event Category: None 
Event ID:   0 
Date:       12/04/2010 
Time:       12:12:38 
User:       N/A 
Computer:   TDXRMISDEV01 
Description: 
System.ArgumentException: Object of type 'TDXDataTypes.AgencyOutput' cannot be converted to type 'AgencyOutput'. 
 
Server stack trace:  
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) 
   at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) 
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) 
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) 
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) 
   at TDXQueueEngine.GenericWebserviceProxy.CallWebService(String webServiceAsmxUrl, String serviceName, String methodName, Object[] args) in C:\CkAdmDev\TDXQueueEngine\TDXQueueEngine\TDXQueueEngine\GenericWebserviceProxy.cs:line 76 
   at TDXQueueEngine.TDXQueueWebserviceItem.Run() in C:\CkAdmDev\TDXQueueEngine\TDXQueueEngine\TDXQueueEngine\TDXQueueWebserviceItem.cs:line 99 
   at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs) 
   at System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(RuntimeMethodHandle md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs) 
   at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink) 
 
Exception rethrown at [0]:  
   at System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(Message reqMsg, Boolean bProxyCase) 
   at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData& msgData) 
   at TDXQueueEngine.TDXQueue.RunProcess.EndInvoke(IAsyncResult result) 
   at TDXQueueEngine.TDXQueue.processComplete(IAsyncResult ar) in C:\CkAdmDev\TDXQueueEngine\TDXQueueEngine\TDXQueueEngine\TDXQueue.cs:line 130 
 
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp. 

这些类引用相同的程序集和相同的版本。构建临时程序集时是否需要包含我的程序集作为引用?如果是这样,怎么做?

谢谢。

更新

似乎最好的解决方案是构建一个可以从 AssemblyX.MyCustomType 映射的例程。相当于 GeneratedAssembly.MyCustomType .

在我的示例中, MyCustomType包含更多类型(它们都应该是生成程序集的一部分),所以看来我需要一种方法来执行此“深度复制”。此外,TDXDataTypes.AgencyOutput 的一些属性是其他类的数组,只是为了让事情更有趣......

我创建了一个 new question用于映射。

请您参考如下方法:

我在本地机器上重现了该问题并修复了该问题..

以下是使自定义对象工作所需执行的操作

你当前的代码是这样的

   object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());    
   MethodInfo mi = wsvcClass.GetType().GetMethod(methodName);    
   return mi.Invoke(wsvcClass, args);    

让我尝试解释问题出现的最可能原因。

当您在 Web 服务中调用程序集中称为“方法名”的方法时,您尝试将所需的参数作为 args[] 传递给函数“CallWebService”
当您尝试传递诸如原始类型(包括字符串)之类的普通参数时,传递的这个 args[] 将成功工作。

但是,当您尝试将自定义对象作为参数传递时,您可能会这样做

这里面做了三件事。
  • 在 CallWebService 函数之外创建一个该类型的对象(使用反射)。当您这样做时,会发生在内部使用临时 dll 名称创建的 customobject 的实例。
  • 一旦设置了对象的属性并将其作为 args 数组中的对象发送到 CallWebService 函数。
  • 你正在尝试通过创建动态 dll 来创建 web 服务的实例。

    对象 wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());

  • 当您最终尝试使用创建的动态程序集的实例调用该方法时
    您正在尝试通过 args 属性传递在步骤 1,2 中创建的自定义对象。

    在调用时,CLR 会尝试查看作为输入传递的自定义对象和被调用的方法是否来自同一个 DLL。

    这显然不是从实现的方式来看的。

    所以以下是应该用来克服这个问题的方法
    您需要使用用于创建 Web 服务实例的相同程序集创建自定义对象程序集...

    我完全实现了这种方法并且效果很好:
    MethodInfo m = type.GetMethod(methodName); 
    ParameterInfo[] pm = m.GetParameters(); 
    object ob; 
    object[] y = new object[1]; 
    foreach (ParameterInfo paraminfo in pm) 
    { 
        ob = this.webServiceAssembly.CreateInstance(paraminfo.ParameterType.Name); 
     
        foreach (PropertyInfo propera in ob.GetType().GetProperties()) 
        { 
            if (propera.Name == "AppGroupid") 
            { 
                propera.SetValue(ob, "SQL2005Tools", null); 
            } 
            if (propera.Name == "Appid") 
            { 
                propera.SetValue(ob, "%", null); 
            } 
        } 
        y[0] = ob; 
    }