Dynamic Web Services Invoking Component Using .Net Reflection Class
From my client B2B practice using Microsoft BizTalk, I had the requirements for Dynamic Web Services Invoking like this:
- Easily to setup and invoke variety web methods of same Web Services from completely different server environments (invoking the same web services using different URLs), such as Development server, QA server, UAT server or Production server.
Loose coupling to the application.
- Easy to maintain and extend from and the code need to be reusable.
- Software version staging support.
BizTalk server has out off box support for the SOAP protocol via the SOAP adapter which can easily specify the URL at design time. However, to invoke the same web services using different URL requires recompile .Net assemblies of solutions, usually are the Orchestration projects using the SOAP adapter.
By leveraging the power of reflection class and attribute class introduced by the .Net and C# (I personally love these classes so much which make the loose coupling, OOP and dynamic coding been a lot easier than before) I have achieved the goal to meet these requirements.
Consuming Web Services Using Web Service Proxy
A web service proxy class can be used to call a web service. Microsoft .Net frame provides a out-off-box utility application wsdl.exe, which can used to generate a proxy class from a WSDL(Web Service Description Language) file. Launch a command prompt window and type “wsdl /?”, you can find how to use this utility application to generate a web service proxy class.
The screen shot of an example of a generated proxy class using this utility against to a wsdl file is shown bellow:
By modifying the code of the constructor, the web service URL can be loaded dynamically from the configuration file at runtime. The code is highlighted in the red rectangle above.
A proxy class can be embedded into a class as an object used to invoke the web services. The code shown bellow, the class AccountDispacher has a member variable SiebleMessage of the type of FbsInboundAccountTopElmt, which is a generated proxy class type.
using System.Xml;
using System.Reflection;
using System.Text;
namespace Dispatcher.AutoNumberDispatcher
{
using Transcore.Integration.CRMNextGen.BTS2004Utilities;
using Transcore.CRMNextGen.Dispatcher.ArgumentTagAttribute;
using Transcore.CRMNextGen.FBS_spcInbound_spcAccount;
///
/// Summary description for AccountDispatcher.
///
[Serializable]
public class AccountDispatcher : AutoNumberDispatcher
{
public FbsInboundAccountTopElmt SiebelMessage = new FbsInboundAccountTopElmt();
private string Id = string.Empty; //Siebel message
private string FBSCustomerId = string.Empty;//Siebel message
public AccountDispatcher( string entity ) : base( entity )
{
SiebelMessage.FbsInboundAccount = new FbsInboundAccount();
SiebelMessage.FbsInboundAccount.Account = new Account();
base.InitialWebserviceData();
}
#region Public method
[AutoNumberWebServiceArgumentTagAttribute( "Account")]
public XmlDocument AcountInvoke()
{
base._attributes = base.RetriveAttrubutes( "AcountInvoke" );
base._arguments = new object [] {
Primary_spcRow_spcId,
object objResults = _InvokeWebservice();
//Populate results
base.AssignResults( SiebelMessage.FbsInboundAccount.Account.Id, SiebelMessage.FbsInboundAccount.Account.FBSCustomerId );
return base._autoNumberWebserviceParameterXmlDoc;
}
#endregion
#region Static public method
//Sample code of usage of Account dispatcher calling from an orchestration
public static XmlDocument AccountWebserviceInterface( string Entity,
string AppID, //Id
string CommonID ) //FBSCustomerId
{
AccountDispatcher accountDisp = new AccountDispatcher( "Account" );
//Populate parameters
XmlDocument xmlDocParameters = new XmlDocument();
xmlDocParameters.LoadXml( string.Format(
"" +
"{0}" +
"" +
"" +
"{1}" +
"" +
"" +
"{2}" +
"" +
""
,Entity.Trim()
,CommonID.Trim()
,AppID.Trim()
) );
XmlNode xmlNode = xmlDocParameters.SelectSingleNode( "/ParametersXml" );
accountDisp.AssingArgumentValues( xmlNode );
accountDisp.SiebelMessage.FbsInboundAccount.Account.Id = AppID.Trim();
accountDisp.SiebelMessage.FbsInboundAccount.Account.FBSCustomerId = CommonID.Trim();
return accountDisp.AcountInvoke();
}
#endregion
}
}
Dynamic invoke a web service proxy class diagram
To dynamically invoke a web service proxy, an abstract class called Dispatcher has been defined. This class has an embedded object _serviceInvokeof type DynamicInvocation.
There are two public method defined in the DynamicInvocationclass, RetrieveMethod and InvokeProcess. The later one is pretty straightforward to understand, which invokes a method of a class using the function of Invoke implemented in the System.Reflection.MethodInfo. The RetrieveMethod function takes the method name of a generated web service proxy class, which is the method you would like to invoke.
How to specify a web method to invoke a web service proxy in a caller class
One of the solutions I have been demonstrated success in our client practice is using the custom attribute class introduced by .Net and a configuration file.
An example bellow shows a configuration file:
<SiebelInboundWevServiceConfigxmlns="http://tempuri.org/SiebelInboundWebServicesConfig.xsd">
<entities>
<entityname="SB2Data"appinstance="SB2_TCE01">
<webserviceParameters>
<argumentcount>2argumentcount>
<listofparamername>
<parametername>d1parametername>
<parametername>d2parametername>
listofparamername>
<listofresults>
<result>Totalresult>
listofresults>
<webservicemethod>AddNumberwebservicemethod>
<webserviceproxy>Transcore.CRMNextGen.DataAccessServicewebserviceproxy>
<webserviceproxy>Transcore.CRMNextGen.BTSWebserverDev01.DataAccessServicewebserviceproxy>
<proxyassemblynamespace>Transcore.CRMNextGen.BTSWebserverDev01proxyassemblynamespace>
webserviceParameters>
entity>
<entityname="CallBack">
<webserviceParameters>
<webservicemethod>RunCallIntInboundwebservicemethod>
<webserviceproxy>Transcore.CRMNextGen.FBS_spcInbound_spcCallback_spcIntegrationwebserviceproxy>
webserviceParameters>
entity>
<entityname="Account">
<webserviceParameters>
<webservicemethod>RunAcctInboundwebservicemethod>
<webserviceproxy>Transcore.CRMNextGen.FBS_spcInbound_spcAccountwebserviceproxy>
webserviceParameters>
entity>
<entityname="Contact">
<webserviceParameters>
<webservicemethod>RunConInboundwebservicemethod>
<webserviceproxy>Transcore.CRMNextGen.FBS_spcInbound_spcContactwebserviceproxy>
webserviceParameters>
entity>
<entityname="Address">
<webserviceParameters>
<webservicemethod>RunAddInboundwebservicemethod>
<webserviceproxy>Transcore.CRMNextGen.FBS_spcInbound_spcAddresswebserviceproxy>
webserviceParameters>
entity>
SiebelInboundWevServiceConfig>
In this example, each “entity” represents a configuration for one web method call; it has two element nodes, the “webserviceParameters” and “webserviceproxy” under the “webserviceParameters” node. The value of the attribute “name” is passed as parameter to a custom attribute class. That custom attribute class will associate to a specific caller interface function. Bellow is a code example for a interface class. In this example, a class AccountDispatcher is derived from AutoNumberDispatcher ( the AutoNumberDispatcher is derived from the base class Dispatcher as described above ), which has an embedded web service proxy class object SiebelMessage of type FbsInboundAccountTopElmt. A method public XmlDocument AcountInvoke implemented in this class has been associated to a custom attribute class which takes string "Account" the as parameter. At run time, a correct .Net assembly will be dynamically loaded and this method will be called.
using System;
using System.Xml;
using System.Reflection;
using System.Text;
namespace Dispatcher.AutoNumberDispatcher
{
using Transcore.Integration.CRMNextGen.BTS2004Utilities;
using Transcore.CRMNextGen.Dispatcher.ArgumentTagAttribute;
using Transcore.CRMNextGen.FBS_spcInbound_spcAccount;
///
/// Summary description for AccountDispatcher.
///
[Serializable]
public class AccountDispatcher : AutoNumberDispatcher
{
public FbsInboundAccountTopElmt SiebelMessage = new FbsInboundAccountTopElmt();
private string Id = string.Empty; //Siebel message
private string FBSCustomerId = string.Empty;//Siebel message
public AccountDispatcher( string entity ) : base( entity )
{
SiebelMessage.FbsInboundAccount = new FbsInboundAccount();
SiebelMessage.FbsInboundAccount.Account = new Account();
base.InitialWebserviceData();
}
#region Public method
[AutoNumberWebServiceArgumentTagAttribute( "Account")]
public XmlDocument AcountInvoke()
{
base._attributes = base.RetriveAttrubutes( "AcountInvoke" );
//Assign argument values
base._arguments = new object [] {
Primary_spcRow_spcId,
Process_spcInstance_spcId,
Object_spcId,
Integration_spcObject,
Entity,
Output_spcId,
Comm_spcProfile,
Siebel_spcOperation_spcObject_spcId,
Workflow_spcName,
Type,
SiebelMessage,
Custom_spcError_spcMessage,
Custom_spcError_spcCode,
Error_spcCode,
Operation,
Error_spcMessage
};
object objResults = _InvokeWebservice();
//Populate results
base.AssignResults( SiebelMessage.FbsInboundAccount.Account.Id, SiebelMessage.FbsInboundAccount.Account.FBSCustomerId );
return base._autoNumberWebserviceParameterXmlDoc;
}
#endregion
#region Static public method
//Sample code of usage of Account dispatcher calling from an orchestration
public static XmlDocument AccountWebserviceInterface( string Entity,
string AppID, //Id
string CommonID ) //FBSCustomerId
{
AccountDispatcher accountDisp = new AccountDispatcher( "Account" );
//Populate parameters
XmlDocument xmlDocParameters = new XmlDocument();
xmlDocParameters.LoadXml( string.Format(
"" +
"{0}" +
"" +
"" +
"{1}" +
"" +
"" +
"{2}" +
"" +
""
,Entity.Trim()
,CommonID.Trim()
,AppID.Trim()
) );
XmlNode xmlNode = xmlDocParameters.SelectSingleNode( "/ParametersXml" );
accountDisp.AssingArgumentValues( xmlNode );
accountDisp.SiebelMessage.FbsInboundAccount.Account.Id = AppID.Trim();
accountDisp.SiebelMessage.FbsInboundAccount.Account.FBSCustomerId = CommonID.Trim();
return accountDisp.AcountInvoke();
}
#endregion
}
}
The source code for the classes and are attached bellow:
o Dispatcher class
using System;
using System.Reflection;
namespace Dispatcher
{
///
/// Summary description for Distaptcher.
///
[Serializable]
abstract public class Distaptcher
{
protected DynamicInvocation _serviceInvoke = null;