using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Refl = System.Reflection;
using System.Net;
using System.Web.Services.Description;
using System.CodeDom;
using System.CodeDom.Compiler;
using Yaulw.Other;
namespace Yaulw.Net
{
///
/// Class to Make Soap Web Services Calls Dynamically (Works only for Microsoft-based Web Services it seems)
///
public class WSCaller
{
#region Private Members
private string _ServiceName = ""; // example: "WebService"
private string _Url = ""; // example: "http://webservice.com/WebService/WebService.asmx"
private Refl.Assembly _webServiceAssembly = null;
// available services
private List _services = new List();
// available types
private Dictionary _availableTypes = new Dictionary();
private bool _InvokeMethodErrorWasThrown = false;
#endregion
#region Construction
///
/// Creates the service invoker using the specified web service.
///
/// Name of Web Service
/// Url of Web Service
public WSCaller(string ServiceName, string ServiceUrl)
{
if (!String.IsNullOrEmpty(ServiceName) && !String.IsNullOrEmpty(ServiceUrl))
{
// Store Service Specifics
_ServiceName = ServiceName;
_Url = ServiceUrl;
// Try Creating the WS Assembly
CreateAssemblyFromWebServiceDescription();
}
else
throw new ArgumentNullException("Service Name or Service Url can not be Null or Empty");
}
#endregion
#region Public Methods
///
/// Check to see if the Web Service Is Available
///
public bool WebServiceAvailable
{
get
{
CreateAssemblyFromWebServiceDescription();
return (_webServiceAssembly != null);
}
}
///
/// Make the Soap Method Call
///
/// Method Name to call
/// parameters to use on call
/// true if called successfully, false otherwise
/// the return value of the Method
public T MakeSoapMethodCall(string MethodName, object[] parameters, out bool bCallSuccess, DelegateCollection.Void_Param1_Exception_Func exception)
{
bCallSuccess = false;
try
{
List Methods = EnumerateServiceMethods(_ServiceName);
if (Methods.Count == 0)
return default(T);
// Try making a CheckActive Call
T result = InvokeMethod(_ServiceName, MethodName, parameters);
bCallSuccess = true;
return result;
}
catch (Exception e) { _InvokeMethodErrorWasThrown = true; if (exception != null) exception(e); }
return default(T);
}
#endregion
#region Private Helpers
///
/// create an assembly from the web service description (WSDL)
///
private void CreateAssemblyFromWebServiceDescription()
{
// Error Occured upon invoke, try rebuilding the WS
if (_InvokeMethodErrorWasThrown && _webServiceAssembly != null)
_webServiceAssembly = null;
// In Order to properly build/rebuild the WS must be available
if (_webServiceAssembly == null)
{
_webServiceAssembly = BuildAssemblyFromWSDL(new Uri(_Url));
if (_webServiceAssembly != null)
{
// Reset
_InvokeMethodErrorWasThrown = false;
// see what service types are available
Type[] types = _webServiceAssembly.GetExportedTypes();
// and save them
foreach (Type type in types)
{
_services.Add(type.FullName);
_availableTypes.Add(type.FullName, type);
}
}
}
}
///
/// Gets a list of all methods available for the specified service.
///
///
///
private List EnumerateServiceMethods(string serviceName)
{
List methods = new List();
if (!_availableTypes.ContainsKey(serviceName))
{
throw new Exception("Service Not Available");
}
else
{
Type type = _availableTypes[serviceName];
// only find methods of this object type (the one we generated)
// we don't want inherited members (this type inherited from SoapHttpClientProtocol)
foreach (Refl.MethodInfo minfo in type.GetMethods(Refl.BindingFlags.Instance | Refl.BindingFlags.Public | Refl.BindingFlags.DeclaredOnly))
methods.Add(minfo.Name);
return methods;
}
}
///
/// Invokes the specified method of the named service.
///
/// The expected return type.
/// The name of the service to use.
/// The name of the method to call.
/// The arguments to the method.
/// The return value from the web service method.
private T InvokeMethod(string serviceName, string methodName, params object[] args)
{
// create an instance of the specified service
// and invoke the method
object obj = null;
Type type = null;
try
{
obj = _webServiceAssembly.CreateInstance(serviceName);
type = obj.GetType();
}
catch (Exception e)
{
throw new Exception(e.Message + " " + "Creation of webServiceAssembly Failed");
}
// Find the Service Point for the Web Service! ~Fix 100 - HTTP 417 Error
ServicePoint servicePoint = ServicePointManager.FindServicePoint(new Uri(_Url));
if (servicePoint.Expect100Continue == true)
servicePoint.Expect100Continue = false;
return (T)type.InvokeMember(methodName, Refl.BindingFlags.InvokeMethod, null, obj, args);
}
///
/// Builds an assembly from a web service description.
/// The assembly can be used to execute the web service methods.
///
/// Location of WSDL (only pass in the main asmx url without the ?wdsl).
/// A web service assembly.
private Refl.Assembly BuildAssemblyFromWSDL(Uri webServiceUri)
{
try
{
if (String.IsNullOrEmpty(webServiceUri.ToString()))
throw new Exception("Web Service Not Found");
ServiceDescriptionImporter descriptionImporter = BuildServiceDescriptionImporter((webServiceUri.ToString() + "?wsdl"));
return CompileAssembly(descriptionImporter);
}
catch (Exception)
{
return null;
}
}
///
/// Builds the web service description importer, which allows us to generate a proxy class based on the
/// content of the WSDL described by the XmlTextReader.
///
/// The http location of the WSDL
/// A ServiceDescriptionImporter that can be used to create a proxy class.
private ServiceDescriptionImporter BuildServiceDescriptionImporter(string wsdlUrl)
{
try
{
WebClient http = new WebClient();
ServiceDescription serviceDescription = ServiceDescription.Read(http.OpenRead(wsdlUrl));
// build an importer, that assumes the SOAP protocol, client binding, and generates properties
ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
descriptionImporter.ProtocolName = "Soap";
descriptionImporter.AddServiceDescription(serviceDescription, null, null);
descriptionImporter.Style = ServiceDescriptionImportStyle.Client;
descriptionImporter.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
return descriptionImporter;
}
catch (Exception)
{
return null;
}
}
///
/// Compiles an assembly from the proxy class provided by the ServiceDescriptionImporter.
///
///
/// An assembly that can be used to execute the web service methods.
private Refl.Assembly CompileAssembly(ServiceDescriptionImporter descriptionImporter)
{
try
{
// a namespace and compile unit are needed by importer
CodeNamespace codeNamespace = new CodeNamespace();
CodeCompileUnit codeUnit = new CodeCompileUnit();
codeUnit.Namespaces.Add(codeNamespace);
ServiceDescriptionImportWarnings importWarnings = descriptionImporter.Import(codeNamespace, codeUnit);
if (importWarnings == 0) // no warnings
{
// create a c# compiler
CodeDomProvider compiler = CodeDomProvider.CreateProvider("CSharp");
// include the assembly references needed to compile
string[] references = new string[2] { "System.Web.Services.dll", "System.Xml.dll" };
CompilerParameters parameters = new CompilerParameters(references);
// compile into assembly
CompilerResults results = compiler.CompileAssemblyFromDom(parameters, codeUnit);
foreach (CompilerError oops in results.Errors)
{
// trap these errors and make them available to exception object
throw new Exception("Compilation Error Creating Assembly");
}
// all done....
return results.CompiledAssembly;
}
else
{
// warnings issued from importers, something wrong with WSDL
throw new Exception("Invalid WSDL");
}
}
catch (Exception)
{
return null;
}
}
#endregion
}
}