Recently there were many tasks on my plate which involved extensive programming using Reflection and CodeDOM. Here is just some fragments of it which might be helpful in understanding how we can gain advantages of Reflection and CodeDOM libraries to make complete dynamic web services consumption.

Lets start with some example:

Legend:

$$variable$$ are variable which are user specific. The name of variable will indicate what value should be provided in that place.

Assuming that there is a web service hosted at http://myweb.com/services/DemoServices.asmx, and we are supposed to build are client consuming the web service, but dynamically. So how will we go about doing it?

$$url$$ = http://myweb.com/services/DemoServices.asmx

Inside the client code, get references to following assemblies:

//For dynamic discovery of web services from url

using System.Web;
using System.Web.Services;
using System.Web.Services.Description;
using System.Web.Services.Discovery;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Web.Services.Protocols;

//For code generation and compilation on the fly

using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

//For dynamic creation of classes from the dynamically generated code, and the invoke methods on the fly.

using System.Reflection;

//Lets do some coding 🙂

//Discover the services from the url first.

DiscoveryClientProtocol protocol = new DiscoveryClientProtocol();
protocol.PreAuthenticate = true;
protocol.UseDefaultCredentials = true;
protocol.Discover($$url$$);
protocol.ResolveOneLevel();
protocol.WriteAll($$DirectoryToPlaceTheFiles$$, “Reference.map”); // generally this is hard-coded so as to match up with the VS IDE generation.

//Now get the service descriptions

ServiceDescriptionCollection services = new ServiceDescriptionCollection();
foreach (DictionaryEntry entry in protocol.References)
{
ContractReference contractRef = entry.Value as ContractReference;
DiscoveryDocumentReference discoveryRef = entry.Value as DiscoveryDocumentReference;
if (contractRef != null)
{
services.Add(contractRef.Contract);
}
}

//Now get the Xml Schemas

XmlSchemas schemas = new XmlSchemas();
foreach (DictionaryEntry entry in protocol.References)
{
SchemaReference schemaRef = entry.Value as SchemaReference;
if (schemaRef != null)
{
schemas.Add(schemaRef.Schema);
}
}

//Now that we have all the essential files, lets start building the service proxies

ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
foreach (ServiceDescription description in services)
{
importer.AddServiceDescription(description, null, null);
}
foreach (XmlSchema schema in schemas)
{
importer.Schemas.Add(schema);
}

//Ok, now we need to compile this stuff into a CLR understandable jargon. Thats assembly.

//Here we make use of CodeDOM libraries to make source code into assemblies.

CodeNamespace codeNamespace = new CodeNamespace($$MyNamespace$$);
CodeCompileUnit codeUnit = new CodeCompileUnit();
codeUnit.Namespaces.Add(codeNamespace);
ServiceDescriptionImportWarnings warnings = importer.Import(codeNamespace, codeUnit);
CodeDomProvider provider = new Microsoft.CSharp.CSharpCodeProvider();

//Write the file to disk

using (StreamWriter sw = new StreamWriter($$CsharpCodeFileName$$, false)) // .cs (source file)
{
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BracingStyle = “C”;
provider.GenerateCodeFromCompileUnit(codeUnit, sw, options);
}

//Read it back into a stream, so as to compile it into assembly

StreamReader filereader= new StreamReader($$CsharpCodeFileName$$);
StringBuilder sb = new StringBuilder();
sb.Append(filereader.ReadToEnd());

//Start compilation

CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler compiler = codeProvider.CreateCompiler();
CompilerParameters parameters = new CompilerParameters();
parameters.OutputAssembly = $$DllFileNameWithPath$$; // keep it inside the folder where you create the .cs (source files)
parameters.GenerateExecutable = false;
parameters.ReferencedAssemblies.Add(“System.dll”);
parameters.ReferencedAssemblies.Add(“System.Xml.dll”);
parameters.ReferencedAssemblies.Add(“System.Data.dll”);
parameters.ReferencedAssemblies.Add(“System.Drawing.dll”);
parameters.ReferencedAssemblies.Add(“System.Web.dll”);
parameters.ReferencedAssemblies.Add(“System.Web.Services.dll”);

//Compile

CompilerResults results = compiler.CompileAssemblyFromSource(parameters, sb.ToString());
if (results.Errors.Count > 0)
{
// Throw your custom exception which will contains all the errors aggregated into a single error
}

// So compilation is done, now load the assembly so that we can execute it

Assembly assembly;
assembly = Assembly.LoadFrom($$DllFileNameWithPath$$);

//Now that we have the assembly, we have to just invoke the method on the specified class

//$$classname$$ will be required

//$$methodname$$ will be required, so as to invoke the right method

//$$parametersHashTable$$ will be required if the method requires arguments, this will be key = parameter name and value = parameter value

foreach (Type type in assembly.GetTypes())
{
if (type.IsClass == true && type.Name.Contains($$classname$$))
{
object ClassObj = Activator.CreateInstance(type);
MethodInfo objMethodInfo = type.GetMethod($$methodname$$);
ParameterInfo[] paramInfo = objMethodInfo.GetParameters();
object[] args = new object[paramInfo.Length];
for (int i = 0; i < paramInfo.Length ; i++)
{
parameterName = paramInfo[i].Name;
if ($$parametersHashTable$$.ContainsKey(parameterName))
args[i] = Convert.ToString($$parametersHashTable$$[parameterName]);
}
}

//Now invoke the method

SoapHttpClientProtocol webProxy = (SoapHttpClientProtocol)ClassObj;
webProxy.PreAuthenticate = true;
webProxy.UseDefaultCredentials = true;
object objResult = objMethodInfo.Invoke(webProxy, args);

//Finally we have all the resultant xml response from the web service into a object called ‘objResult’

Lots of things can be avoided from the above code to make it look cleaner and short. Refactoring will be helpful. This was just as it is from my test code 🙂

Note:

1. Do not forget to pass on the credentials will discovering from the url, and also will invoking the method on the dynamically created class. If you skip this you may face “401 Access Denied“.

That’s all for this episode. Happy coding.

-Bugs!

Advertisements

One Response to “Web-Services with Reflection in .NET”


Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: