Dynamically Invoking a Web Service
One of the promises of web services is that you can dynamically discover new services and invoke them. Without venturing into the debate of appropriate discovery and delivery mechanisms (UDDI, DISCO, WS-Discovery, WS-Transfer, WS-MetaDataExchange), we will focus on the dynamic invocation aspect and will assume that you have already obtained the URL to the WSDL document of interest.
Perhaps you have lots of web references generated and you want to programmatically invoke them. You can rifle through the executing assembly's types and invoke a method via reflection. If each web method has the same signature and you just want to invoke lots of web services in a sprinkler head fashion, you could pre-gen the type and then just change the URL on the proxy:
WebServiceProxy.localhost.WebService s = new WebServiceProxy.localhost.WebService();
s.Url = "https://foo/webservice.asmx";
Console.WriteLine(s.HelloWorld());
s.Url = "https://bar/webservice.asmx";
Console.WriteLine(s.HelloWorld());
That's a pretty contrived example and not very applicable... it is highly unlikely that you will have lots of methods with a common signature in pre-generated proxies.
Instead of using pre-generated assemblies, perhaps you want to generate the assembly on the fly and invoke it. In other words, instead of right-clicking and using the "Add Web Reference" dialog or using WSDL.exe to create a proxy, perhaps you want to point your code at a URL and invoke a method without previously creating a proxy.
The easiest way to dynamically invoke a web service is to leverage the work of others. Christian Weyer's DynWsLib is a great tool that accomplishes the above task very nicely.
If you are a glutton for punishment, then you can leverage the System.Web.Services.Description.ServiceDescriptionImporter. The example on that page is good, but there's a better example of using ServiceDescriptionImporter in the System.Web.Services.Description.WebReference class documentation. Below, I modified that example and added some code to invoke a HelloWorld web method.
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Security.Permissions;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Discovery;
using System.Xml;
using System.Xml.Serialization;
class Program
{
[SecurityPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
static void Main()
{
System.Net.WebClient client = new System.Net.WebClient();
System.IO.Stream stream = client.OpenRead("https://localhost/webservicedemo/webservice.asmx?wsdl");
// Get a WSDL file describing a service.
ServiceDescription description = ServiceDescription.Read(stream);
// Initialize a service description importer.
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12"; // Use SOAP 1.2.
importer.AddServiceDescription(description, null, null);
// Report on the service descriptions.
Console.WriteLine("Importing {0} service descriptions with {1} associated schemas.",
importer.ServiceDescriptions.Count, importer.Schemas.Count);
// 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)
{
// Generate and print the proxy code in C#.
CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
// Compile the assembly with the appropriate references
string[] assemblyReferences = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };
CompilerParameters parms = new CompilerParameters(assemblyReferences);
CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
foreach (CompilerError oops in results.Errors)
{
Console.WriteLine("========Compiler error============");
Console.WriteLine(oops.ErrorText);
}
//Invoke the web service method
object o = results.CompiledAssembly.CreateInstance("WebService");
Type t = o.GetType();
Console.WriteLine(t.InvokeMember("HelloWorld", System.Reflection.BindingFlags.InvokeMethod, null, o, null));
}
else
{
// Print an error message.
Console.WriteLine("Warning: " + warning);
}
}
You can see that there really isn't that much code required to dynamically invoke the web service method without requiring a pre-existing SoapHttpClientProtocol. However, you will want to flesh out other details of this before considering putting this into an application, such as using intelligent caching to avoid the lookups and assembly generation.
If you are considering writing this type of code just to support development and testing efforts, then you should have a look at the free WebServiceStudio. This is a great utility for dynamically creating web service proxies and invoking them based on WSDLs. Once you play around with it as well as the code above, you can easily figure out how many of the features were implemented. Here are a couple screen shots to whet your appetite.
WebServiceStudio allows you to dynamically invoke web services based on the WSDL description of the service.
After invocation, you can also inspect the message trace.
Comments
- Anonymous
March 10, 2008
PingBack from http://www.k2thought.com/?p=26 - Anonymous
April 13, 2015
I was trying out your code. It builds without error. But when I run it I am getting error at these lines://Invoke the web service method object o = results.CompiledAssembly.CreateInstance("WebService"); Type t = o.GetType();o is returning null.I will be kind if you can help me out? - Anonymous
April 23, 2015
Hey Jagdish H, you must replace "WebService" by the actual name of your service - Anonymous
June 18, 2015
Two things:
- Are any changes required in order for it to work with an SSL-based Web Service?
- The WebServiceStudio link is broken. Your code worked like a charm, much appreciated.
- Anonymous
June 24, 2015
Rick - I have no idea, I haven't used this technology in many years. My recommendation would be to use Web API to create RESTful services. If you need SOAP web service support, you should investigate using WCF.