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

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

要求:

  1. 客户在运行时提供 SOAP Web 服务的 WSDL,即从文件共享位置选择 WSDL 文件。
  2. 使用 WSDL,在 UI 上调用客户选择的方法并处理响应。

我无法使用 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 中使用)
object retVal = instance.GetType().GetMethod(operationName)
                        .Invoke(instance, operationParameters);   // Invoke

我尝试对操作名称进行硬编码,从 WSDL 手动解析,但随后在参数处失败了。它需要一个包含如下层次结构的复杂类型:

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

后续步骤:

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

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

谢谢, 开发

最佳答案

本文中描述了执行此操作的解决方案:

Generate proxy code for a web service dynamically

虽然您可以打开该链接并阅读它,但我将代码包含在此处,以防该链接随时失效:

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.

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.

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;
}

关于c# - 在运行时使用和调用 SOAP Web 服务 - 来自 WSDL 文件的动态 Web 服务客户端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27145485/

相关文章:

asp.net - 如何在 ASP.NET MVC 网站上执行定期工作?

javascript - 使用 Ajax 或 Silverlight 调用 Web 服务?哪个表现最好?

ruby-on-rails - Web 服务的多部分响应

c# - ServiceStack REST 服务中的自定义异常处理

c# - 异常时通知用户

javascript - 获取下拉菜单的 Angular 值

javascript - 解析网站中的 JSON 数据

c# - WP7动态更新UI而不阻塞线程

c# - 无法从其他类更改表单中的 textBox.text - C# - VS 2010

带有 IIS 的 ASP.NET Core - 不允许使用 HTTP 动词