问题描述
要求:
- 客户在运行时提供 SOAP Web 服务的 WSDL,即从文件共享位置选择 WSDL 文件.
- 使用 WSDL,并调用客户在 UI 上选择的方法并处理响应.
我无法使用 MetadataExchangeClient,因为不会托管 WSDL.
实施:
var serviceDescription = ServiceDescription.Read(@"C:Contacts.WSDL");var metadataSection = 新的 MetadataSection{方言 = MetadataSection.ServiceDescriptionDialect,标识符 = serviceDescription.TargetNamespace,元数据 = 服务描述};var metadataSections = new List{元数据部分};var metadatSet = new MetadataSet(metadataSections);var wsdlImporter = new WsdlImporter(metadatSet);var services = wsdlImporter.ImportAllEndpoints();
路障:
- 上述代码根本无法提取服务端点.因此,我必须手动创建一个服务端点.
- 我无法在步骤中列出上述 WSDL 中包含的所有方法及其关联的输入/输出(将在下面的变量 operationName 和 operationParameters 中使用)
object retVal = instance.GetType().GetMethod(operationName).Invoke(instance, operationParameters);//调用
我尝试通过硬编码操作名称,从 WSDL 手动解析,但随后在参数处失败.它期望包含如下层次结构的复杂类型:
ContactInput --> ListOfContacts --> Contact --> FirstName, LastName
后续步骤:
如果有人能帮我解决障碍,那么我可以继续上述方法.
否则,我必须开始研究在运行时使用 svcutil.exe
谢谢,开发
本文描述了一个解决方案:
尽管您可以打开该链接并阅读它,但我在此处包含了代码,以防该链接随时失效:
此方法返回 Web 服务公开的操作列表.我使用 ServiceDescription 来实现这一点,因为我无法仅反映生成的汇编中的 Web 方法名称.有了可用的操作名称,剩下的就是找出每个方法的输入和返回参数.
public string[] GenerateProxyAssembly(){//创建一个 WebRequest 对象并获取 Web 服务的 WSDL 文件HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(this.uri);request.Credentials = CredentialCache.DefaultCredentials;HttpWebResponse 响应 = (HttpWebResponse)request.GetResponse();System.IO.Stream 流 = response.GetResponseStream();//读取下载的WSDL文件ServiceDescription desc = ServiceDescription.Read(stream);//找出web服务暴露的操作数//将操作的名称存储在字符串数组中//仅迭代暴露为的第一个绑定//其余的绑定将具有相同的编号int i = 0;绑定绑定 = desc.Bindings[0];OperationBindingCollection opColl = binding.Operations;foreach(opColl 中的操作绑定操作){listOfOperations[i++] = operation.Name;}//初始化一个ServiceDescriptionImporter对象ServiceDescriptionImporter importer = new ServiceDescriptionImporter();//设置协议为SOAP 1.1importer.ProtocolName = "Soap12";//将Style设置为Client以生成客户端代理代码importer.Style = ServiceDescriptionImportStyle.Client;//将ServiceDescription添加到Importer对象importer.AddServiceDescription(desc, null, null);importer.CodeGenerationOptions = CodeGenerationOptions.GenerateNewAsync;//初始化 CODE DOM 树,我们将在其中导入//服务描述导入器代码命名空间 nm = 新代码命名空间();CodeCompileUnit unit = new CodeCompileUnit();unit.Namespaces.Add(nm);//生成客户端代理代码ServiceDescriptionImportWarnings 警告 = importer.Import(nm, unit);如果(警告== 0){//将CodeDOMProvider设置为C#以生成C#中的代码System.IO.StringWriter sw = new System.IO.StringWriter();CodeDomProvider provider = CodeDomProvider.CreateProvider(C#");provider.GenerateCodeFromCompileUnit(unit, sw, new CodeGeneratorOptions());//创建临时文件集合//临时文件夹的路径是硬编码的TempFileCollection coll = new TempFileCollection(@"C:wmpub empFiles");coll.KeepFiles = false;//设置临时程序集的CompilerParametersstring[] 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;//将生成的代码编译成程序集//CompilerResults results = provider.CompileAssemblyFromDom(param, unitArr);CompilerResults 结果= provider.CompileAssemblyFromSource(param, sw.ToString());this.assem = results.CompiledAssembly;}//返回Web服务公开的操作列表返回列表操作;}
该方法返回ParameterInfo[]列表中的输入参数.要获取输出参数,只需将 MethodInfo 类上的 GetParamters() 调用替换为 ReturnParameter 属性并将其放入新方法中.将这 3 个方法放在一个 dll 中,并从任何客户端应用程序添加对它的引用.就这样.只需提供 URL 并使用 Web 服务,任何 Web 服务.每次要使用新的 Web 服务时,您都不必经历创建代理文件的过程.
public ParameterInfo[] ReturnInputParameters(string methodName){//创建Web服务类型的实例//////////////去做///////////////////////////从wsdl动态获取web服务的名称Object o = this.assem.CreateInstance(服务");类型服务 = o.GetType();参数信息[] paramArr = null;//获取生成的//程序集中所有可用公共方法的列表MethodInfo[] infoArr = service.GetMethods();foreach(infoArr 中的 MethodInfo 信息){//获取输入参数信息//需要的网络方法if (methodName.Equals(info.Name)){paramArr = info.GetParameters();}}返回参数Arr;}
Requirement:
- Customer to give the SOAP Web-service's WSDL at runtime i.e pick the WSDL file from a file share location.
- Consume the WSDL, and call the Method chosen by the Customer on the UI and handle the response.
I cannot use the MetadataExchangeClient as the WSDL will not be hosted.
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:
- The above code could not extract the service endpoints at all. So, I had to manually create an service endpoint.
- 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)
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 :
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
Thanks,Dev
There is a solution for doing this described in this article:
Generate proxy code for a web service dynamically
Although you can open that link and read it, I include the code here, just in case that link dies anytime:
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 empFiles");
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;
}
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;
}
这篇关于在运行时使用和调用 SOAP WebServices - 来自 WSDL 文件的动态 Web 服务客户端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!