在运行时使用和调用SOAP WebServices - 来自WSDL文件的动态Web服务客户端

时间:2014-11-26 09:28:29

标签: c# asp.net web-services soap wsdl

要求:

  1. 客户在运行时提供SOAP Web服务的WSDL,即从文件共享位置选择WSDL文件。
  2. 使用WSDL,并在UI上调用Customer选择的Method并处理响应。
  3. 我无法使用MetadataExchangeClient,因为不会托管WSDL。

    实施

    var serviceDescription = ServiceDescription.Read(@"C:\Contacts.WSDL");
    var metadataSection = new MetadataSection
    {
    Dialect = MetadataSection.ServiceDescriptionDialect,
    Identifier = serviceDescription.TargetNamespace,
    Metadata = serviceDescription
    };
    
    var metadataSections = new List<MetadataSection> {metadataSection};
    var metadatSet = new MetadataSet(metadataSections);
    var wsdlImporter = new WsdlImporter(metadatSet);
    var services = wsdlImporter.ImportAllEndpoints();
    

    路障:

    1. 上述代码根本无法提取服务端点。所以,我不得不手动创建一个服务端点。
    2. 我无法在步骤中列出上述WSDL中包含的所有方法及其相关的输入/输出(将在下面的变量operationName和operationParameters中使用)
    3. object retVal = instance.GetType().GetMethod(operationName)
                              .Invoke(instance, operationParameters);   // Invoke
      

      我尝试通过硬编码操作名称,手动从WSDL解析,但随后它在参数上失败了。它期望包含层次结构的复杂类型如下:

        

      ContactInput - &gt; ListOfContacts - &gt;联系 - &gt; FirstName,LastName

      后续步骤:

      如果有人可以帮我解决障碍,那么我可以继续采用上述方法。

      否则,我必须开始研究在运行时使用svcutil.exe

      谢谢, 开发

1 个答案:

答案 0 :(得分:8)

有一个解决方案可以执行本文中描述的操作:

Generate proxy code for a web service dynamically

虽然你可以打开那个链接并阅读它,但我在这里包含了代码,以防链接在任何时候死亡:

  

此方法返回Web服务公开的操作列表。我已经使用ServiceDescription实现了这一点,因为我无法仅反映生成的Web方法名称。有了可用的操作名称,剩下的就是找出每种方法的输入和返回参数。

public string[] GenerateProxyAssembly()
{
  //create a WebRequest object and fetch the WSDL file for the web service
  HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(this.uri);
  request.Credentials = CredentialCache.DefaultCredentials;
  HttpWebResponse response = (HttpWebResponse)request.GetResponse();
  System.IO.Stream stream = response.GetResponseStream();

  //read the downloaded WSDL file
  ServiceDescription desc = ServiceDescription.Read(stream);

  //find out the number of operations exposed by the web service
  //store the name of the operations inside the string array
  //iterating only through the first binding exposed as
  //the rest of the bindings will have the same number
  int i = 0;
  Binding binding = desc.Bindings[0];
  OperationBindingCollection opColl = binding.Operations;
  foreach (OperationBinding operation in opColl)
  {
    listOfOperations[i++] = operation.Name;
  }

  //initializing a ServiceDescriptionImporter object
  ServiceDescriptionImporter importer = new ServiceDescriptionImporter();

  //set the protocol to SOAP 1.1
  importer.ProtocolName = "Soap12";

  //setting the Style to Client in order to generate client proxy code
  importer.Style = ServiceDescriptionImportStyle.Client;

  //adding the ServiceDescription to the Importer object
  importer.AddServiceDescription(desc, null, null);
  importer.CodeGenerationOptions = CodeGenerationOptions.GenerateNewAsync;

  //Initialize the CODE DOM tree in which we will import the 
  //ServiceDescriptionImporter
  CodeNamespace nm = new CodeNamespace();
  CodeCompileUnit unit = new CodeCompileUnit();
  unit.Namespaces.Add(nm);

  //generating the client proxy code
  ServiceDescriptionImportWarnings warnings = importer.Import(nm, unit);

  if (warnings == 0)
  {
    //set the CodeDOMProvider to C# to generate the code in C#
    System.IO.StringWriter sw = new System.IO.StringWriter();
    CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
    provider.GenerateCodeFromCompileUnit(unit, sw, new CodeGeneratorOptions());

    //creating TempFileCollection
    //the path of the temp folder is hardcoded
    TempFileCollection coll = new TempFileCollection(@"C:\wmpub\tempFiles");
    coll.KeepFiles = false;

    //setting the CompilerParameters for the temporary assembly
    string[] refAssembly = { "System.dll", "System.Data.dll", 
      "System.Web.Services.dll", "System.Xml.dll" };
    CompilerParameters param = new CompilerParameters(refAssembly);
    param.GenerateInMemory = true;
    param.TreatWarningsAsErrors = false;
    param.OutputAssembly = "WebServiceReflector.dll";
    param.TempFiles = coll;

    //compile the generated code into an assembly
    //CompilerResults results = provider.CompileAssemblyFromDom(param, unitArr);
    CompilerResults results 
       = provider.CompileAssemblyFromSource(param, sw.ToString());
    this.assem = results.CompiledAssembly;
  }

  //return the list of operations exposed by the web service
  return listOfOperations;
}
  

此方法返回ParameterInfo []列表中的输入参数。要获取输出参数,只需使用ReturnParameter属性替换对MethodInfo类的GetParamters()的调用,并将其放入新方法中。将这3个方法放在dll中,并从任何客户端应用程序添加对它的引用。这就是全部。只需提供URL并使用Web服务,任何Web服务。每次要使用新的Web服务时,都不必完成创建代理文件的过程。

public ParameterInfo[] ReturnInputParameters(string methodName)
{
  //create an instance of the web service type
  //////////////to do/////////////////////////
  //get the name of the web service dynamically from the wsdl
  Object o = this.assem.CreateInstance("Service");
  Type service = o.GetType();
  ParameterInfo[] paramArr = null;

  //get the list of all public methods available in the generated //assembly
  MethodInfo[] infoArr = service.GetMethods();

  foreach (MethodInfo info in infoArr)
  {
  //get the input parameter information for the
  //required web method
    if (methodName.Equals(info.Name))
    {
      paramArr = info.GetParameters();
    }
  }

  return paramArr;
}