我正在This Site使用代码动态调用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;
}
}
}
当我使用内置类型时这很好用,但对于我自己的类,我得到了这个:
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.
这些类引用相同的程序集和相同的版本。在构建临时组件时,是否需要将组件作为参考?如果是这样,怎么样?
感谢。
更新
似乎最好的解决方案是构建一个可以从AssemblyX.MyCustomType
映射到等效GeneratedAssembly.MyCustomType
的例程。
在我的示例中,MyCustomType
包含更多类型(应该都是生成的程序集的一部分),因此看起来我需要一个方法来执行“深度复制”。此外,TDXDataTypes.AgencyOutput的一些属性是其他类的数组,只是为了让事情更有趣......
我为映射创建了new question。
答案 0 :(得分:6)
我在我的本地机器上重现了这个问题并解决了这个问题..
您需要执行以下操作才能使自定义对象正常工作
Ur当前代码是这样的
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
MethodInfo mi = wsvcClass.GetType().GetMethod(methodName);
return mi.Invoke(wsvcClass, args);
让我试着解释问题的最可能原因。
当你在webservice中调用一个名为“methodname”的程序集中的方法时,你试图将args []所需的参数传递给函数“CallWebService” 当您尝试传递包括字符串等基本类型的常规参数时,传递的args []将成功运行。
但是当你尝试将自定义对象作为参数传递时,这就是你可能正在做的事情
在此完成了三件事。
您正在尝试通过创建动态网址来创建网络服务的实例。
object wsvcClass = results.CompiledAssembly.CreateInstance(foundType.ToString());
当您最终尝试使用创建的动态程序集的实例调用该方法时 您正尝试通过args属性传递在步骤1,2中创建的自定义对象。
在调用时,CLR会尝试查看作为输入传递的customobject和正在调用的方法是否来自同一个DLL。
这显然不是执行方式。
以下是应该用来克服这个问题的方法 您需要使用与创建Web服务实例相同的程序集创建自定义对象程序集...
我完全实现了这种方法,结果很好:
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;
}
答案 1 :(得分:3)
动态编译的类将不等于您直接引用的类,因此您无法将其转换为另一个。要使两个类相等,它们必须来自同一个程序集(或者你可以自己进行反序列化)。
我会考虑使用像AutoMapper这样的东西在两个类之间进行映射。您将设置从编译类型到引用类型的映射,然后映射类。
[编辑 - 编译的代码]
使用AutoMapper的示例:
object ret = DynWebservice.CallWebService(...);
Mapper.CreateMap(ret.GetType(), typeof(TDXDataTypes.AgencyOutput));
TDXDataTypes.AgencyOutput ao = (TDXDataTypes.AgencyOutput)Mapper.Map(ret, ret.GetType(), typeof(TDXDataTypes.AgencyOutput));
答案 2 :(得分:2)
对于传递自定义对象,一种方法是对自定义对象进行de /序列化。另请参阅How to: Enable a Web Service to Send and Receive Large Amounts of Data和C# – Dynamically Invoke Web Service At Runtime
答案 3 :(得分:0)
我已经使用此处的代码(http://www.crowsprogramming.com/archives/66)来动态调用Web服务,并且能够通过将类型序列化为XML然后再返回来转换类型。在我的例子中,我试图访问的类型(T)是由WSDL.EXE生成的类文件,指向我正在动态调用的Web服务。
public T ConvertType<T>(object input)
{
XmlSerializer serializer = new XmlSerializer(input.GetType());
XmlSerializer deserializer = new XmlSerializer(typeof(T));
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
serializer.Serialize(sw, input);
}
using (StringReader sr = new StringReader(sb.ToString()))
{
return (T)deserializer.Deserialize(sr);
}
}