C#中異地調用WEB Service的通用類庫

下面這個庫是一個關於異地調用WEB Service的通用類庫,它主要利用C#中Reflection的機制,通過獲取遠程服務器的WSDL文件,在本地生成一個代理類,獲得通過在本機上引用Web Service生成代理類的相同效果。

本類庫從www.codeproject.com上下載,筆者曾在一個實驗項目上應用過,覺得很不錯,在此與大家共享:

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Web.Services.Description;
using System.Web.Services.Discovery;
using System.Web.Services.Protocols;
using Microsoft.CSharp;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.Text;
using System.Net;
using System.Security.Cryptography;

// DynamicWebServiceProxy - This sample is provided as is!
// Please notice that this code is only a technology demo
// and should not be included unedited und untested into any serious project.
// -------------------------------------------------------------------------------------
// The usage of the library is shown in the WSLibTester project in the same download file.
//
// (C) Christian Weyer, 2002-2003. All rights reserved.

namespace eYesoft.Tools.WebServices
{
 public enum ProtocolEnum
 {
  HttpGet,
  HttpPost,
  HttpSoap
 }

 // For feedback/questions/comments: [email protected]
 public class DynamicWebServiceProxy
 {
  private Assembly ass = null;
  private object proxyInstance = null;
  private string wsdl;
  private string wsdlSource;
  private string typeName;
  private string methodName;
  private string protocolName = "Soap";
  private ArrayList methodParams = new ArrayList();
  private string proxySource;
  private ServiceDescriptionImporter sdi;

  public DynamicWebServiceProxy()
  {
  }

  public DynamicWebServiceProxy(string wsdlLocation)
  {
   wsdl = wsdlLocation;
   BuildProxy();
  }
  
  public DynamicWebServiceProxy(string wsdlLocation, string inTypeName)
  {
   wsdl = wsdlLocation;
   typeName = inTypeName;
   BuildProxy();
  }

  public DynamicWebServiceProxy(string wsdlLocation, string inTypeName, string inMethodName)
  {
   wsdl = wsdlLocation;
   typeName = inTypeName;
   methodName = inMethodName;
   BuildProxy();
  }

  public DynamicWebServiceProxy(string wsdlLocation, string inTypeName, string inMethodName, params object[] inMethodParams)
  {
   wsdl = wsdlLocation;
   typeName = inTypeName;
   methodName = inMethodName;
   methodParams = null;
   methodParams = new ArrayList(inMethodParams);
   BuildProxy();
  }

 
  public void AddParameter(object param)
  {
   methodParams.Add(param);
  }

  public object InvokeCall()
  {
   try
   {
    MethodInfo mi = proxyInstance.GetType().GetMethod(methodName);
    object result = mi.Invoke(proxyInstance, (object[])methodParams.ToArray(typeof(object)));
   
    return result;
   }
   catch(Exception e)
   {
    Console.WriteLine(e.Message);
    return null;
   }
  }

  public string Url
  {
   get
   {
    PropertyInfo propInfo = proxyInstance.GetType().GetProperty("Url");
    object result = propInfo.GetValue(proxyInstance, null);
    
    return (string)result;
   }
   set
   {
    PropertyInfo propInfo = proxyInstance.GetType().GetProperty("Url");
    propInfo.SetValue(proxyInstance, value,
     BindingFlags.NonPublic | BindingFlags.Static |
     BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetField,
     null, null, null
     );
   }
  }


  // TODO: move the init process to an explicit method Init() ...
  public string WSDL
  {
   get
   {
    return wsdl;
   }
   set
   {
    wsdl = value;
    ResetInternalState();
    BuildProxy();
   }
  }

  public string TypeName
  {
   get
   {
    return typeName;
   }
   set
   {
    typeName = value;
   }
  }

  public string MethodName
  {
   get
   {
    return methodName;
   }
   set
   {
    methodName = value;
   }
  }

  public ProtocolEnum ProtocolName
  {
   get
   {
    switch(protocolName)
    {
     case "HttpGet":
      return ProtocolEnum.HttpGet;
     case "HttpPost":
      return ProtocolEnum.HttpPost;
     case "Soap":
      return ProtocolEnum.HttpSoap;
     default:
      return ProtocolEnum.HttpSoap;
    }
   }
   set
   {
    switch(value)
    {
     case ProtocolEnum.HttpGet:
      protocolName = "HttpGet";
      break;
     case ProtocolEnum.HttpPost:
      protocolName = "HttpPost";
      break;
     case ProtocolEnum.HttpSoap:
      protocolName = "Soap";
      break;
    }
   }
  }

  public void ClearCache(string WSDL)
  {
   // clear the cached assembly file for this WSDL
   try
   {
    string path = Path.GetTempPath();
    string newFilename = path + GetMd5Sum(WSDL) + "_eYesoft_tmp.dll";

    File.Delete(newFilename);
   }
   catch(Exception e)
   {
    e = e;
   }
  }


  private void GetWsdl(string source)
  {
   // this could be a valid WSDL representation
   if(source.StartsWith("<?xml version") == true)
   {
    wsdlSource = source;
    return; 
   }
   // this is a URL to the WSDL
   else if(source.StartsWith("http://") == true)
   {
    wsdlSource = GetWsdlFromUri(source);
    return;
   }
       
   // try to get from local file system
   wsdlSource = GetWsdlFromFile(source);
   return;
  }
  
  private string GetWsdlFromUri(string uri)
  {
   WebRequest req = WebRequest.Create(uri);
   WebResponse result = req.GetResponse();
     
   Stream ReceiveStream = result.GetResponseStream();
   Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
   StreamReader sr = new StreamReader(ReceiveStream, encode);
     
   string wsdlSource = sr.ReadToEnd();
   sr.Close();
     
   return wsdlSource;
  }
  
  private string GetWsdlFromFile(string fileFullPathName)
  {
   FileInfo fi = new FileInfo(fileFullPathName);
     
   if(fi.Extension == "wsdl")
   {
    FileStream fs = new FileStream(fileFullPathName, FileMode.Open, FileAccess.Read);
    StreamReader sr = new StreamReader(fs);
       
    char[] buffer = new char[(int)fs.Length];
    sr.ReadBlock(buffer, 0, (int)fs.Length);
    sr.Close();
       
    return new string(buffer);
   }
    
   throw new Exception("This is not a WSDL file");
  }

  private Assembly BuildAssemblyFromWsdl(string strWsdl)
  {
   // Use an XmlTextReader to get the Web Service description
   StringReader  wsdlStringReader = new StringReader(strWsdl);
   XmlTextReader tr = new XmlTextReader(wsdlStringReader);
   ServiceDescription sd = ServiceDescription.Read(tr);
   tr.Close();

   // WSDL service description importer
   CodeNamespace cns = new CodeNamespace("eYesoft.Tools.WebServices.DynamicProxy");
   sdi = new ServiceDescriptionImporter();
   //sdi.AddServiceDescription(sd, null, null);
   
   // check for optional imports in the root WSDL
   CheckForImports(wsdl);

   sdi.ProtocolName = protocolName;
   sdi.Import(cns, null);

   // change the base class
   CodeTypeDeclaration ctDecl = cns.Types[0];
   cns.Types.Remove(ctDecl);
   ctDecl.BaseTypes[0] = new CodeTypeReference("eYesoft.Tools.WebServices.SoapHttpClientProtocolEx");
   cns.Types.Add(ctDecl);

   // source code generation
   CSharpCodeProvider cscp = new CSharpCodeProvider();
   ICodeGenerator icg = cscp.CreateGenerator();
   StringBuilder srcStringBuilder = new StringBuilder();
   StringWriter sw = new StringWriter(srcStringBuilder);
   icg.GenerateCodeFromNamespace(cns, sw, null);
   proxySource = srcStringBuilder.ToString();
   sw.Close();

   // assembly compilation
   CompilerParameters cp = new CompilerParameters();
   cp.ReferencedAssemblies.Add("System.dll");
   cp.ReferencedAssemblies.Add("System.Xml.dll");
   cp.ReferencedAssemblies.Add("System.Web.Services.dll");
   cp.ReferencedAssemblies.Add("System.Data.dll");
   cp.ReferencedAssemblies.Add(System.Reflection.Assembly.
    GetExecutingAssembly().Location);
 
   cp.GenerateExecutable = false;
   cp.GenerateInMemory = false;
   cp.IncludeDebugInformation = false;

   ICodeCompiler icc = cscp.CreateCompiler();
   CompilerResults cr = icc.CompileAssemblyFromSource(cp, proxySource);
   
   if(cr.Errors.Count > 0)
    throw new Exception(string.Format(@"Build failed: {0} errors", cr.Errors.Count));

   ass = cr.CompiledAssembly;
   
   //rename temporary assembly in order to cache it for later use
   RenameTempAssembly(cr.PathToAssembly);
   
   // create proxy instance
   proxyInstance = CreateInstance(typeName);
   
   return ass;
  }
  
  private object CreateInstance(string objTypeName)
  {
   // check whether the type is already created or not
   if (objTypeName == "" || objTypeName == null)
   {
    foreach (Type ty in ProxyAssembly.GetTypes())
    {
     if(ty.BaseType == typeof(eYesoft.Tools.WebServices.SoapHttpClientProtocolEx))
     {
      objTypeName = ty.Name;
      break;
     }
    }
   }
   Type t = ass.GetType("eYesoft.Tools.WebServices.DynamicProxy." + objTypeName);
   
   return Activator.CreateInstance(t);
  }
  
  private void RenameTempAssembly(string pathToAssembly)
  {   
   string path = Path.GetDirectoryName(pathToAssembly);
   string newFilename = path + @"/" + GetMd5Sum(wsdl) + "_eYesoft_tmp.dll";
   
   File.Copy(pathToAssembly, newFilename);
  }

  private void ResetInternalState()
  {
   typeName = "";
   methodName = "";
   protocolName = "Soap";
   methodParams.Clear();
   sdi = null;
  }

  private void BuildProxy()
  {
   // inject SOAP extensions in (client side) ASMX pipeline
   InjectExtension(typeof(SoapMessageAccessClientExtension));
   
   //check cache first
   if (!CheckCache())
   {
    GetWsdl(wsdl);
    BuildAssemblyFromWsdl(wsdlSource);
   }
  }

  private bool CheckCache()
  {
   string path = Path.GetTempPath() + GetMd5Sum(wsdl) + "_eYesoft_tmp.dll";

   if(File.Exists(path))
   {
    ass = Assembly.LoadFrom(path);
    // create proxy instance
    proxyInstance = CreateInstance(typeName);

    return true;
   }
   
   return false;
  }
  
  private void CheckForImports(string baseWSDLUrl)
  {
   DiscoveryClientProtocol dcp = new DiscoveryClientProtocol();
   dcp.DiscoverAny(baseWSDLUrl);
   dcp.ResolveAll();

   foreach (object osd in dcp.Documents.Values)
   {
    if (osd is ServiceDescription) sdi.AddServiceDescription((ServiceDescription)osd, null, null);;
    if (osd is XmlSchema) sdi.Schemas.Add((XmlSchema)osd);
   }
  }

  static void InjectExtension(Type extension)
  {
   Assembly assBase;
   Type webServiceConfig;
   object currentProp;
   PropertyInfo propInfo;
   object[] value;
   Type myType;
   object[] objArray;
   object myObj;
   FieldInfo myField;

   try
   {
    assBase = typeof(SoapExtensionAttribute).Assembly;
    webServiceConfig =
     assBase.GetType("System.Web.Services.Configuration.WebServicesConfiguration");

    if (webServiceConfig == null)
     throw new Exception("Error ...");

    currentProp = webServiceConfig.GetProperty("Current",
     BindingFlags.NonPublic | BindingFlags.Static |
     BindingFlags.Instance | BindingFlags.Public
     ).GetValue(null, null);
    propInfo = webServiceConfig.GetProperty("SoapExtensionTypes",
     BindingFlags.NonPublic | BindingFlags.Static |
     BindingFlags.Instance | BindingFlags.Public
     );
    value = (object[])propInfo.GetValue(currentProp, null);
    myType = value.GetType().GetElementType();
    objArray = (object[])Array.CreateInstance(myType, (int)value.Length + 1);
    Array.Copy(value, objArray, (int)value.Length);

    myObj = Activator.CreateInstance(myType);
    myField = myType.GetField("Type",
     BindingFlags.NonPublic | BindingFlags.Static |
     BindingFlags.Instance | BindingFlags.Public
     );
    myField.SetValue(myObj, extension,
     BindingFlags.NonPublic | BindingFlags.Static |
     BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetField,
     null, null
     );
    objArray[(int)objArray.Length - 1] = myObj;
    propInfo.SetValue(currentProp, objArray,
     BindingFlags.NonPublic | BindingFlags.Static |
     BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty,
     null, null, null
     );
   }
   catch (Exception e)
   {
    e = e;
   }
  }

  private string GetMd5Sum(string str)
  {
   // First we need to convert the string into bytes, which
   // means using a text encoder
   Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

   // Create a buffer large enough to hold the string
   byte[] unicodeText = new byte[str.Length * 2];
   enc.GetBytes(str.ToCharArray(), 0, str.Length, unicodeText, 0, true);

   // Now that we have a byte array we can ask the CSP to hash it
   MD5 md5 = new MD5CryptoServiceProvider();
   byte[] result = md5.ComputeHash(unicodeText);

   // Build the final string by converting each byte
   // into hex and appending it to a StringBuilder
   StringBuilder sb = new StringBuilder();
   for (int i=0;i<result.Length;i++)
   {
    sb.Append(result[i].ToString("X2"));
   }

   return sb.ToString();
  }

  public string SoapRequest
  {
   get
   {
    PropertyInfo propInfo = proxyInstance.GetType().GetProperty("SoapRequest");
    object result = propInfo.GetValue(proxyInstance, null);
    System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
    
    return enc.GetString((byte[])result);
   }
  }

  public string SoapResponse
  {
   get
   {
    PropertyInfo propInfo = proxyInstance.GetType().GetProperty("SoapResponse");
    object result = propInfo.GetValue(proxyInstance, null);
    System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
    
    return enc.GetString((byte[])result);
   }
  }

  public Assembly ProxyAssembly
  {
   get
   {
    return ass;
   }
  }

 }
}

如需要本人項目的源代碼,可與本人聯繫!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章