从自己的脚本语言动态调用SOAP服务

时间:2010-08-11 07:52:53

标签: .net visual-c++ soap soap-client webservice-client

我的应用程序有自己的脚本语言,我无法摆脱(编写了许多客户特定的脚本)。现在,我的客户正在询问是否可以从该脚本语言中调用SOAP服务。当然,需要调用的SOAP服务对于每个客户都是不同的。这给我留下了几个选择:

  • 使用WSDL实用程序生成客户特定的SOAP客户端代理,并将特定于客户的逻辑放入我的应用程序中
  • 使用WSDL实用程序生成客户特定的SOAP客户端代理,将客户特定的逻辑放在客户特定的DLL中,并预见应用程序可以通用方式调用插件的插件系统
  • 编写动态生成SOAP调用的通用模块

在我的案例中,前2个选项不是真正的替代选择,因为我不希望在应用程序或客户特定的DLL中使用任何特定于客户的逻辑。

对我来说,从长远来看,第三个选项是最好的,因为它允许我的顾问同事通过我的脚本语言调用SOAP服务,而不需要做任何客户特定的开发。动态添加函数到我的脚本语言不是问题,生成动态SOAP调用是。

我首先查看了WSDL实用程序的输出。然后我开始删除东西,直到它不再起作用。以下代码仍然有效:

[System::CodeDom::Compiler::GeneratedCodeAttribute(L"wsdl", L"4.0.30319.1"), 
System::Diagnostics::DebuggerStepThroughAttribute, 
System::ComponentModel::DesignerCategoryAttribute(L"code"),
System::Web::Services::WebServiceBindingAttribute(Name=L"MyOwnScriptingSoapClient", Namespace=L"http://microsoft.com/webservices/")]
public ref class MyWebService : public System::Web::Services::Protocols::SoapHttpClientProtocol
   {
    public:
      MyWebService() {}

    public:
      [System::Web::Services::Protocols::SoapDocumentMethodAttribute(L"http://microsoft.com/webservices/GetPrimeNumbers", RequestNamespace=L"http://microsoft.com/webservices/", 
       ResponseNamespace=L"http://microsoft.com/webservices/", Use=System::Web::Services::Description::SoapBindingUse::Literal, ParameterStyle=System::Web::Services::Protocols::SoapParameterStyle::Wrapped)]
      System::String^  GetPrimeNumbers(System::Int32 max);
  };

inline System::String^  MyWebService::GetPrimeNumbers(System::Int32 max) {
    cli::array< System::Object^  >^  results = this->Invoke(L"GetPrimeNumbers", gcnew cli::array< System::Object^  >(1) {max});
    return (cli::safe_cast<System::String^  >(results[0]));
}

可以通过设置Url属性动态地设置Web服务的URL,但我找不到使方法名称动态化的方法。

添加这样的通用方法似乎仍然有效:

...
[System::Web::Services::Protocols::SoapDocumentMethodAttribute(L"http://microsoft.com/webservices/GetPrimeNumbers", RequestNamespace=L"http://microsoft.com/webservices/", 
 ResponseNamespace=L"http://microsoft.com/webservices/", Use=System::Web::Services::Description::SoapBindingUse::Literal, ParameterStyle=System::Web::Services::Protocols::SoapParameterStyle::Wrapped)]
cli::array< System::Object^  >^  CallWs(cli::array< System::Object^  >^ args);
...

inline cli::array< System::Object^  >^  MyWebService::CallWs(cli::array< System::Object^  >^ args) {
    cli::array< System::Object^  >^  results = this->Invoke(L"GetPrimeNumbers", args);
    return results;

但是一旦我删除GetPrimeNumbers方法,该调用就不再起作用并报告以下错误:

Unhandled Exception: System.ArgumentException: GetPrimeNumbers Web Service method name is not valid.
   at System.Web.Services.Protocols.SoapHttpClientProtocol.BeforeSerialize(WebRequest request, String methodName, Object[] parameters)
   at System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(String methodName, Object[] parameters)
   at MyWebService.CallWs(Object[] args)
   at main(Int32 argc, SByte** argv)
   at _mainCRTStartup()

此外,更改SoapDocumentMethodAttribute属性中的Web服务名称(例如,更改为GetPrimo)会产生同样的错误。

因此,我的问题:

  • 继续这条路径是否有意义,即查看WSDL生成的逻辑试图“概括”对(任何)SOAP服务的调用,或者这根本不会工作?
  • 是否还有其他以动态方式生成SOAP调用的好方法(使用.Net)?
  • 或者是自己创建XML(Soap Envelope)来进行SOAP调用的唯一方法吗?
  • 是否有机会找到一些我可以继续使用的示例代码?

提前致谢, 帕特里克

3 个答案:

答案 0 :(得分:1)

您可以使用脚本语言中的机制来调用外部.NET程序集。您可以使用反射来查找函数并调用它们。类似于插件在许多应用程序中的工作方式。

这不仅允许客户呼叫外部Web服务,还可以用于许多其他增强功能。

或者如果您不想依赖于您的客户编写.NET程序集,您可以通过询问用户SOAP消息名称,参数名称,类型和值,服务URL等来自行生成SOAP请求。但我认为这不是一条简单的道路,我承认我自己从来没有这样做过。

答案 1 :(得分:1)

您可以尝试使用内置编译器动态生成动态代码。
直接从代码(使用代码提供程序,如http://msdn.microsoft.com/en-us/library/microsoft.csharp.csharpcodeprovider.aspx)或直接构建类(参见 http://msdn.microsoft.com/en-us/library/system.codedom.compiler.codedomprovider.aspx示例)

生成代码可以通过多种方式完成:

答案 2 :(得分:0)

如果不了解脚本语言的功能,很难回答。

一个想法可能是使用另一种语言创建更强大的服务,该服务具有工厂模式以调用相关客户服务并以非客户特定的格式返回到您的脚本信息。

当然,假设您可以从您的特定于域的脚本语言中首先调用另一个程序。

也许像Managed Extensibility Framework (or MEF for short)这样的东西也适合你。