代码之家  ›  专栏  ›  技术社区  ›  cjk

调用webservice而不添加webreference-具有复杂类型

  •  1
  • cjk  · 技术社区  · 15 年前

    我在用密码 This Site 动态调用WebService。

    [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 为了地图。

    4 回复  |  直到 11 年前
        1
  •  3
  •   Mikael Svenson    15 年前

    动态编译的类将不等于直接引用的类,因此不能将一个类强制转换为另一个类。要使两个类相等,它们必须来自同一个程序集(或者您可以滚动自己的反序列化)。

    我会考虑使用 AutoMapper 在两个类之间映射。您将设置从编译类型到引用类型的映射,然后映射类。

    [编辑-编译的代码]

    使用automapper的示例:

    object ret = DynWebservice.CallWebService(...);
    Mapper.CreateMap(ret.GetType(), typeof(TDXDataTypes.AgencyOutput));
    TDXDataTypes.AgencyOutput ao = (TDXDataTypes.AgencyOutput)Mapper.Map(ret, ret.GetType(), typeof(TDXDataTypes.AgencyOutput));
    
        2
  •  6
  •   BenMorel Manish Pradhan    11 年前

    我在本地机器上复制了这个问题并修复了那个问题。

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

    你现在的密码是这样的

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

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

    当您在程序集中调用在webservice中称为“methodname”的方法时,尝试将该方法所需的参数作为args[]传递给函数“callwebservice” 当您尝试传递常规参数(如包含字符串的基元类型)时,传递时此参数[]将成功工作。

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

    这里面有三件事。

    1. 在callwebservice函数外部创建该类型的对象(使用反射)。当您这样做时,会发生一个customobject实例,该实例在内部使用临时dll名称创建。
    2. 一旦设置了对象的属性集,并将其作为args数组中的对象发送到callwebservice函数。
    3. 您正试图通过创建动态dll来创建webservice的实例。

      对象wsvcclass=results.compiledAssembly.createInstance(foundType.toString());

    当您最终尝试使用创建的动态程序集实例调用该方法时 您正试图通过args属性传递在步骤1、2创建的customobject。

    在调用时,clr尝试查看作为输入传递的customobject和正在调用的方法是否来自同一个dll。

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

    所以下面是解决这个问题的方法 你需要用创建webservice实例时使用的程序集创建自定义对象程序集…

    我完全实施了这个方法,结果很好:

    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;
    }
    
        3
  •  2
  •   Kamran Khan    15 年前

    对于传递自定义对象,一种方法可以是反序列化自定义对象。也看到 How to: Enable a Web Service to Send and Receive Large Amounts of Data C# – Dynamically Invoke Web Service At Runtime

        4
  •  0
  •   Greg Biles    14 年前

    我用过这里的代码( http://www.crowsprogramming.com/archives/66 )动态调用一个web服务,并能够通过将其序列化为xml然后再次转换类型。在我的例子中,我试图获取的类型(t)在wsdl.exe生成的类文件中,该类文件指向我动态调用的web服务。

        public T ConvertType<T>(object input)
        {
            XmlSerializer serializer = new XmlSerializer(input.GetType());
            XmlSerializer deserializer = new XmlSerializer(typeof(T));
    
            StringBuilder sb = new StringBuilder();
            using (StringWriter sw = new StringWriter(sb))
            {
                serializer.Serialize(sw, input);
            }
    
            using (StringReader sr = new StringReader(sb.ToString()))
            {
                return (T)deserializer.Deserialize(sr);
            }
        }