我正在使用此站点上的代码动态调用Web服务.
[SecurityPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
public static object CallWebService(string webServiceAsmxUrl, string serviceName, string methodName, object[] args)
{
System.Net.WebClient client = new System.Net.WebClient();
//-Connect To the web service
using (System.IO.Stream stream = client.OpenRead(webServiceAsmxUrl + "?wsdl"))
{
//--Now read the WSDL file describing a service.
ServiceDescription description = ServiceDescription.Read(stream);
///// LOAD THE DOM /////////
//--Initialize a service description importer.
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12"; // Use SOAP 1.2.
importer.AddServiceDescription(description, null, null);
//--Generate a proxy client. importer.Style = ServiceDescriptionImportStyle.Client;
//--Generate properties to represent primitive values.
importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
//--Initialize a Code-DOM tree into which we will import the service.
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit1 = new CodeCompileUnit();
unit1.Namespaces.Add(nmspace);
//--Import the service into the Code-DOM tree. This creates proxy code
//--that uses the service.
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1);
if (warning == 0) //--If zero then we are good to go
{
//--Generate the proxy code
CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
//--Compile the assembly proxy with the appropriate references
string[] assemblyReferences = new string[5] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" };
CompilerParameters parms = new CompilerParameters(assemblyReferences);
CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
//-Check For Errors
if (results.Errors.Count > 0)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError oops in results.Errors)
{
sb.AppendLine("========Compiler error============");
sb.AppendLine(oops.ErrorText);
}
throw new System.ApplicationException("Compile Error Occured calling webservice. " + sb.ToString());
}
//--Finally, Invoke the web service method
Type foundType = null;
Type[] types = results.CompiledAssembly.GetTypes();
foreach (Type type in types)
{
if (type.BaseType == typeof(System.Web.Services.Protocols.SoapHttpClientProtocol))
{
Console.WriteLine(type.ToString());
foundType = type;
}
}
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
MethodInfo mi = wsvcClass.GetType().GetMethod(methodName);
return mi.Invoke(wsvcClass, args);
}
else
{
return null;
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我使用内置类型时,这工作正常,但对于我自己的类,我得到这个:
Event Type: Error
Event Source: TDX Queue Service
Event Category: None
Event ID: 0
Date: 12/04/2010
Time: 12:12:38
User: N/A
Computer: TDXRMISDEV01
Description:
System.ArgumentException: Object of type 'TDXDataTypes.AgencyOutput' cannot be converted to type 'AgencyOutput'.
Server stack trace:
at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at TDXQueueEngine.GenericWebserviceProxy.CallWebService(String webServiceAsmxUrl, String serviceName, String methodName, Object[] args) in C:\CkAdmDev\TDXQueueEngine\TDXQueueEngine\TDXQueueEngine\GenericWebserviceProxy.cs:line 76
at TDXQueueEngine.TDXQueueWebserviceItem.Run() in C:\CkAdmDev\TDXQueueEngine\TDXQueueEngine\TDXQueueEngine\TDXQueueWebserviceItem.cs:line 99
at System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
at System.Runtime.Remoting.Messaging.StackBuilderSink.PrivateProcessMessage(RuntimeMethodHandle md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
at System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(Message reqMsg, Boolean bProxyCase)
at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData& msgData)
at TDXQueueEngine.TDXQueue.RunProcess.EndInvoke(IAsyncResult result)
at TDXQueueEngine.TDXQueue.processComplete(IAsyncResult ar) in C:\CkAdmDev\TDXQueueEngine\TDXQueueEngine\TDXQueueEngine\TDXQueue.cs:line 130
For more information, see Help and Support Center at http://go.microsoft.com/fwlink/events.asp.
Run Code Online (Sandbox Code Playgroud)
这些类引用相同的程序集和相同的版本.在构建临时组件时,是否需要将组件作为参考?如果是这样,怎么样?
谢谢.
更新
似乎最好的解决方案是构建一个可以映射AssemblyX.MyCustomType到等效的例程GeneratedAssembly.MyCustomType.
在我的示例中,MyCustomType包含更多类型(应该都是生成的程序集的一部分)所以看来我需要一个方法来执行"深度复制".此外,TDXDataTypes.AgencyOutput的一些属性是其他类的数组,只是为了让事情变得更有趣......
我已经为映射创建了一个新问题.
我在我的本地机器上复制了这个问题并解决了这个问题.
以下是使自定义对象起作用所需的操作
Ur当前代码是这样的
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
MethodInfo mi = wsvcClass.GetType().GetMethod(methodName);
return mi.Invoke(wsvcClass, args);
Run Code Online (Sandbox Code Playgroud)
让我试着解释一下问题的最可能原因.
当我在webservice中调用一个名为"methodname"的程序集中的方法时,你试图将args []所需的参数传递给函数"CallWebService"当你尝试传递时,传递的args []将成功运行包括字符串在内的基本类型等常规参数.
但是,当您尝试将自定义对象作为参数传递时,这就是您可能正在做的事情
在这方面做了三件事.
您正在尝试通过创建动态dll来创建Web服务的实例.
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
当您最终尝试使用创建的动态程序集的实例调用该方法时,您尝试通过args属性传递在步骤1,2中创建的customobject.
在调用时,CLR尝试查看作为输入传递的customobject和正在调用的方法是否来自同一DLL.
这显然不是从实现的方式来看.
以下是应该用于解决问题的方法U需要使用您用于创建webservice实例的相同程序集创建自定义对象程序集...
我完全实现了这种方法并且效果很好:
MethodInfo m = type.GetMethod(methodName);
ParameterInfo[] pm = m.GetParameters();
object ob;
object[] y = new object[1];
foreach (ParameterInfo paraminfo in pm)
{
ob = this.webServiceAssembly.CreateInstance(paraminfo.ParameterType.Name);
foreach (PropertyInfo propera in ob.GetType().GetProperties())
{
if (propera.Name == "AppGroupid")
{
propera.SetValue(ob, "SQL2005Tools", null);
}
if (propera.Name == "Appid")
{
propera.SetValue(ob, "%", null);
}
}
y[0] = ob;
}
Run Code Online (Sandbox Code Playgroud)