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

时间:2022-01-03 09:52:16

Requirement:

需求:

  1. Customer to give the SOAP Web-service's WSDL at runtime i.e pick the WSDL file from a file share location.
  2. 客户在运行时提供SOAP Web服务的WSDL,即从文件共享位置选择WSDL文件。
  3. Consume the WSDL, and call the Method chosen by the Customer on the UI and handle the response.
  4. 使用WSDL,并在UI上调用Customer选择的Method并处理响应。

I cannot use the MetadataExchangeClient as the WSDL will not be hosted.

我无法使用MetadataExchangeClient,因为不会托管WSDL。

Implementation:

执行:

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();

Road Blocks:

路障:

  1. The above code could not extract the service endpoints at all. So, I had to manually create an service endpoint.
  2. 上面的代码根本无法提取服务端点。所以,我不得不手动创建一个服务端点。
  3. I could not list out all the methods contained in the above WSDL and its associated Inputs/Outputs in the step (to be used in the variable operationName and operationParameters below)
  4. 我无法在步骤中列出上述WSDL中包含的所有方法及其关联的输入/输出(将在下面的变量operationName和operationParameters中使用)
object retVal = instance.GetType().GetMethod(operationName)
                        .Invoke(instance, operationParameters);   // Invoke

I tried by hard coding the operation name, manually parsed from the WSDL, but then it failed at the parameters. It's expecting a complex type containing the hierarchy as below :

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

ContactInput --> ListOfContacts --> Contact --> FirstName, LastName

ContactInput - > ListOfContacts - > Contact - > FirstName,LastName

Next Steps:

下一步:

If someone could help me fix the roadblocks, then I can proceed with the above approach.

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

Else, I have to start researching on using the svcutil.exe at runtime

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

Thanks, Dev

谢谢,开发

1 个解决方案

#1


5  

There is a solution for doing this described in this article:

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

Generate proxy code for a web service dynamically

动态生成Web服务的代理代码

Although you can open that link and read it, I include the code here, just in case that link dies anytime:

虽然您可以打开该链接并阅读它,但我在此处包含代码,以防链接在任何时候死亡:

This method returns the list of operations exposed by the web service. I have used ServiceDescription to achieve that as I was not able to reflect only the web method names from the generated assmebly. With the operation names available, all that remains is to find out the input and return parameters for each method.

此方法返回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;
}

This method returns the input parameters in ParameterInfo[] list. To get the output parameter, just replace the call to GetParamters() on MethodInfo class with ReturnParameter property and put that inside a new method. Put these 3 methods inside a dll and add a reference to it from any client application. That's all. Just provide the URL and consume the web service, any web service. You don't have to go through the procedure of creating a proxy file every time you want to consume a new web service.

此方法返回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;
}

#1


5  

There is a solution for doing this described in this article:

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

Generate proxy code for a web service dynamically

动态生成Web服务的代理代码

Although you can open that link and read it, I include the code here, just in case that link dies anytime:

虽然您可以打开该链接并阅读它,但我在此处包含代码,以防链接在任何时候死亡:

This method returns the list of operations exposed by the web service. I have used ServiceDescription to achieve that as I was not able to reflect only the web method names from the generated assmebly. With the operation names available, all that remains is to find out the input and return parameters for each method.

此方法返回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;
}

This method returns the input parameters in ParameterInfo[] list. To get the output parameter, just replace the call to GetParamters() on MethodInfo class with ReturnParameter property and put that inside a new method. Put these 3 methods inside a dll and add a reference to it from any client application. That's all. Just provide the URL and consume the web service, any web service. You don't have to go through the procedure of creating a proxy file every time you want to consume a new web service.

此方法返回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;
}