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 } }