293 lines
11 KiB
C#
293 lines
11 KiB
C#
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
|
|
{
|
|
/// <remarks>
|
|
/// Class to Make Soap Web Services Calls Dynamically (Works only for Microsoft-based Web Services it seems)
|
|
/// </remarks>
|
|
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<string> _services = new List<string>();
|
|
|
|
// available types
|
|
private Dictionary<string, Type> _availableTypes = new Dictionary<string, Type>();
|
|
|
|
private bool _InvokeMethodErrorWasThrown = false;
|
|
|
|
#endregion
|
|
|
|
#region Construction
|
|
|
|
/// <summary>
|
|
/// Creates the service invoker using the specified web service.
|
|
/// </summary>
|
|
/// <param name="ServiceName">Name of Web Service</param>
|
|
/// <param name="ServiceUrl">Url of Web Service</param>
|
|
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
|
|
|
|
/// <summary>
|
|
/// Check to see if the Web Service Is Available
|
|
/// </summary>
|
|
public bool WebServiceAvailable
|
|
{
|
|
get
|
|
{
|
|
CreateAssemblyFromWebServiceDescription();
|
|
return (_webServiceAssembly != null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Make the Soap Method Call
|
|
/// </summary>
|
|
/// <param name="MethodName">Method Name to call</param>
|
|
/// <param name="parameters">parameters to use on call</param>
|
|
/// <param name="bCallSuccess">true if called successfully, false otherwise</param>
|
|
/// <returns>the return value of the Method</returns>
|
|
public T MakeSoapMethodCall<T>(string MethodName, object[] parameters, out bool bCallSuccess, DelegateCollection.Void_Param1_Exception_Func exception)
|
|
{
|
|
bCallSuccess = false;
|
|
try
|
|
{
|
|
List<String> Methods = EnumerateServiceMethods(_ServiceName);
|
|
if (Methods.Count == 0)
|
|
return default(T);
|
|
|
|
// Try making a CheckActive Call
|
|
T result = InvokeMethod<T>(_ServiceName, MethodName, parameters);
|
|
bCallSuccess = true;
|
|
return result;
|
|
}
|
|
catch (Exception e) { _InvokeMethodErrorWasThrown = true; if (exception != null) exception(e); }
|
|
return default(T);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Helpers
|
|
|
|
/// <summary>
|
|
/// create an assembly from the web service description (WSDL)
|
|
/// </summary>
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a list of all methods available for the specified service.
|
|
/// </summary>
|
|
/// <param name="serviceName"></param>
|
|
/// <returns></returns>
|
|
private List<string> EnumerateServiceMethods(string serviceName)
|
|
{
|
|
List<string> methods = new List<string>();
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Invokes the specified method of the named service.
|
|
/// </summary>
|
|
/// <typeparam name="T">The expected return type.</typeparam>
|
|
/// <param name="serviceName">The name of the service to use.</param>
|
|
/// <param name="methodName">The name of the method to call.</param>
|
|
/// <param name="args">The arguments to the method.</param>
|
|
/// <returns>The return value from the web service method.</returns>
|
|
private T InvokeMethod<T>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds an assembly from a web service description.
|
|
/// The assembly can be used to execute the web service methods.
|
|
/// </summary>
|
|
/// <param name="webServiceUri">Location of WSDL (only pass in the main asmx url without the ?wdsl).</param>
|
|
/// <returns>A web service assembly.</returns>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="wsdlUrl">The http location of the WSDL</param>
|
|
/// <returns>A ServiceDescriptionImporter that can be used to create a proxy class.</returns>
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Compiles an assembly from the proxy class provided by the ServiceDescriptionImporter.
|
|
/// </summary>
|
|
/// <param name="descriptionImporter"></param>
|
|
/// <returns>An assembly that can be used to execute the web service methods.</returns>
|
|
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
|
|
}
|
|
}
|