Click here to Skip to main content
Licence 
First Posted 8 Dec 2005
Views 60,780
Bookmarked 49 times

A web service as a framework for simplifying development and deployment of business functions

By | 27 Dec 2005 | Article
Getting the most out of XML, web service, and typed data sets.

Introduction

In the company where I work we use a lot of web services to communicate between the front-end web applications and the various back-end systems. Now, I am a little tired of creating and deploying ASP.NET web services. Don't get me wrong, I like ASP.NET as a tool for developing web services. However, almost every time our website needs enhancements I end up delivering a new ASP.NET web service.

The development work is fun, but I also need to modify the configuration files and create installer projects and test them in 3 to 5 different environments. There is always a chance of calling me to figure out the production problems on the day of deployment (which means coming to office at 5 am or on weekends).

It would be nice to have a handful of web services running on each server such that they (the web services) never need to be updated. It would be even nicer if we can add new business functions by simply copying new DLLs or replacing the existing DLLs on the server. I know it is possible, so don't tell me it is not :-). In fact, I am going to describe one of my solutions below. I hope you like the idea or give me some feedback on how to improve it.

DataSetService, my approach

I am looking for a way to use a single web service on each server and let it handle all the SOAP communication for the existing and new business functions. What I am trying to do is separate the following:

  • SOAP communication between the client and the server.
  • Interfaces of the business functions (input and output data types).
  • Implementation of the business functions.

The SOAP communication part will be handled by the web service I have proposed. The interfaces to the clients will be defined in a collection of .NET DLLs, these DLLs contain typed data sets and nothing else. The business functions will be implemented in another collection of .NET DLLs. I call my special web service DataSetService because both the input and the output of the main method are .NET DataSet objects.

Each request from the client side will consist of a key string representing a specific business function and XML data (a .NET DataSet object, actually) representing the corresponding input for that function. My DataSetService will:

  1. Load the .NET DLL implementing the requested business function.
  2. Convert the input XML data into a typed data set for the said business function.
  3. Invoke the method for that business function in the loaded DLL.
  4. Return the output XML data (again, a .NET DataSet object) to the client.

The mapping of the business function and the associated .NET DLL that implements that function is stored in the AppSettings section of the web.config file for DataSetService.

Now, why do we want to do things this way? The advantages are:

  1. Simplification of the development of new business functions- Forget about the SOAP protocol and IIS, all we need to do is develop a .NET DLL that has a class with a default constructor and a public method in the class implementing the new business function. This public method will take a typed data set object as input and return a typed data set object as output. The typed data sets should be contained in a separate .NET DLL which will also be used by the clients who need to invoke the business function and process the return data.
  2. Simplification of deployment of new or modified business functions- The DataSetService will never need to be modified after its initial deployment (in theory).
    1. For deploying new business functions, you need to copy the new .NET DLLs into a folder on the server and modify the web.config file for DataSetService to add new entries for mapping the new code to the new business functions.
    2. When you want to change the implementation of the existing business functions, just replace the corresponding .NET DLLs in the corresponding server folder.
    3. If you want to change the interfaces as well, then you need to replace the .NET DLLs containing the definitions of the input and output typed data sets (the client code may need to be changed accordingly).
  3. Simplification of the client programs- Note that with this approach, the client programs need only to call one web service for all business functions instead of calling multiple web services.

Please note that our DataSetService has no hard-coded knowledge of the business functions and their interfaces, it invokes the correct business function based on the user input and the information stored in its web.config file. Here is a sample entry in the web.config file that defines the sample business function Math.Service:

<appSettings>
  <add key="Math.Service" value="D:\Test\MathService\bin\MathService.dll, 
                                                 MathService.Svc, Run" />
</appSettings>

The string "Math.Service" is the key for the business function, its value is a comma-delimited string of three items. The first item is the path of the .NET DLL that implements Math.Service. The second and the third items are class name and method name in the DLL corresponding to the business function. One .NET DLL can implement multiple business functions, each of the business functions will need a unique entry in the web.config file. The main method of the DataSetService has the following signature:

[WebMethod] public DataSet RunService(DataSet oInputDataSet, 
                                          string sServiceKey);

The RunService method takes a .NET DataSet object and a key string as input, and returns a .NET DataSet object as output. Clients invoke a business function by calling the RunService web method specifying a key string, such as "Math.Service", and a .NET DataSet object. The content of the .NET DataSet object from the client should match the typed data set demanded by the .NET DLL that implements the business function.

How does RunService work behind the scenes? When a business function is requested for the first time, the key string provided by the client will be used to get the information stored in the web.config file: the DLL path, the class name, and the method name. Then the .NET DLL with the given path will be loaded and .NET reflection will be used to get the Assembly object, the Type object, and the MethodInfo object for this business function (and these objects will be stored in a Hashtable for later use). Finally, RunService will convert the input data to the desired typed data set and dynamically invoke the correct method in the loaded DLL and return the output data to the client. That's it!

Here is the implementation of the RunService method, where FindService is a private method responsible for retrieving and processing type information from the business DLLs:

public DataSet RunService(DataSet oInputDataSet, string sServiceKey)
{
  object[] pServiceInfo = FindService(sServiceKey,1);
  Type oType = (Type)pServiceInfo[1];
  MethodInfo oMethodInfo = (MethodInfo)pServiceInfo[2];
  object oInstance = Activator.CreateInstance(oType);
  object oParameter = 
    Activator.CreateInstance(oMethodInfo.GetParameters()[0].ParameterType);
  CopyXmlData(oInputDataSet, (DataSet)oParameter);
  object oOutput = oMethodInfo.Invoke(oInstance,new object[1]{oParameter});
  return (DataSet)oOutput;
}

Interfaces and business functions

As mentioned earlier, I want to use the typed data sets as input and output types for the business functions. This is because the typed data sets can provide type-safety. All the business functions will be implemented by the methods that take one typed data set as input and return another typed data set.

A typed data set is a user defined class that is derived from the .NET DataSet class. Almost any kind of data can be represented as a typed data set. We can pass a typed data set object to our main web method RunService. It is also easy to convert the output of the RunService method to the desired typed data set object. Although typed data sets and business functions can be implemented within the same .NET DLL, I recommend using a separate .NET DLL to contain the typed data sets for the input and output types of a business function.

To create a DLL for typed data sets, you need to open a .NET library project. To add a typed data set to the library project, right click the project in Solution Explorer and select "Add --> Add Components ...". Then select the "Data Set" icon in the new dialog box. After typing a name for the typed data set, click the "Open" button, an .xsd (XML schema) file will be added to your project. Now you need to modify the .xsd file using the XML Designer (or do it manually) to add data fields, the data fields can be simple value types or complex types. After you have defined all the fields in the .xsd file, build the project to generate the code for your typed data sets. For details on how to create your typed data sets using XML Designer, click here.

To implement a business function, you create another .NET DLL. The DLL should reference the (typed data sets) DLL you created earlier. It should contain a class with a default constructor and a public method that takes the input typed data set as parameter and returns an object of the output typed data set. Here is the signature of the public method for the sample business function Math.Service:

public MathDataSet.MathData Run(MathDataSet.MathData oData);

Note that MathDataSet.MathData is a typed data set defined in a separate .NET DLL and it is used as both input and output types of the Run method.

If you have used web services before, you know that you can query web service WSDL files to find out what input type is expected by the web service and what output type will be returned, etc. With DataSetService, the WSDL file does not tell you much because the RunService method is used for all the business functions. Here is the type information portion for the RunService method in the WSDL file:

<s:element name="RunService">
  <s:complexType>
    <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="oInputDataSet">
        <s:complexType>
          <s:sequence>
            <s:element ref="s:schema" /> 
            <s:any /> 
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:element minOccurs="0" maxOccurs="1" 
                     name="sServiceKey" type="s:string" /> 
    </s:sequence>
  </s:complexType>
</s:element>
<s:element name="RunServiceResponse">
  <s:complexType>
    <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" 
                 name="RunServiceResult">
        <s:complexType>
          <s:sequence>
            <s:element ref="s:schema" /> 
            <s:any /> 
          </s:sequence>
        </s:complexType>
      </s:element>
    </s:sequence>
  </s:complexType>
</s:element>

Not very helpful, isn't it? But there is another way to get more detailed type information from DataSetService.

Suppose you have implemented a set of business functions on a server using DataSetService as described in this article. The GetServiceInfo web method returns information about a supported business function with the given key string. For example, if the clients call GetServiceInfo with input key string "Math.Service", it will return the following XML data:

<ServiceInfo>
  <AssemblyPath>D:\Test\MathService\bin\MathService.dll</AssemblyPath>
  <ClassName>MathService.Svc</ClassName>
  <MethodName>Run</MethodName>
  <InputName>oData</InputName>
  <InputType>MathDataSet.MathData</InputType>
  <InputTypeAssemblyPath>D:\Test\MathService\bin\MathDataSet.DLL
  </InputTypeAssemblyPath>
  <OutputType>MathDataSet.MathData</OutputType>
  <OutputTypeAssemblyPath>D:\Test\MathService\bin\MathDataSet.DLL
  </OutputTypeAssemblyPath>
</ServiceInfo>

From the above XML, you can see which DLL implements the business function, what are the input and output types, and which DLL contains the typed data sets, etc.

There are two more web methods in DataSetService that may be useful for your clients. The GetInputTypeModule method returns the binary content of the DLL that contains the input typed data set of the business function with a given key string. The GetOutputTypeModule method returns the binary content of the DLL that contains the output typed data set of the business function with a given key string.

The sample programs

The following projects were included in the download zip files:

  1. DataSetService - This is the main web service project.
  2. DataSetServiceUtility - A library of common utility functions.
  3. MathDataSet - This library defines a typed data set MathDataSet.MathData.
  4. MathSerivce - This is the library that implements Math.Service, it supports add, subtract, multiply, and divide operations. The MathDataSet.MathData class is used as both input and output types.
  5. MathClient - This is an ASP.NET application that demonstrates how to invoke the business function Math.Service via DataSetService.

To use the sample programs, you need to create virtual directories for DataSetService and MathClient on the localhost, then open the URL "http://localhost/MathClient/MathForm.aspx".

Here is a sample input XML for Math.Service:

<MathData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                          xmlns="http://tempuri.org/Math.xsd">
  <InputData>
    <Operation>Add</Operation> 
    <Argument>1</Argument> 
    <Argument>2</Argument> 
    <Argument>3</Argument> 
    <Argument>4</Argument> 
    <Argument>5</Argument> 
    <Argument>6</Argument> 
    <Argument>7</Argument> 
    <Argument>8</Argument> 
    <Argument>9</Argument> 
    <Argument>10</Argument> 
  </InputData>
</MathData>

Here is a sample output XML for Math.Service:

<MathData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                           xmlns="http://tempuri.org/Math.xsd">
  <InputData>
    <Operation>Add</Operation> 
    <Argument>1</Argument> 
    <Argument>2</Argument> 
    <Argument>3</Argument> 
    <Argument>4</Argument> 
    <Argument>5</Argument> 
    <Argument>6</Argument> 
    <Argument>7</Argument> 
    <Argument>8</Argument> 
    <Argument>9</Argument> 
    <Argument>10</Argument> 
  </InputData>
  <OutputData>
    <Result>55</Result> 
  </OutputData>
</MathData>

Invoke business functions locally

What if one business function hosted by DataSetService needs to use another business function hosted by the same instance of DataSetService? It will be silly to call the other business function via a SOAP protocol. In this case, the DLL that implements the business function should use the RunService method in the library DataSetServiceUtility to call the other business function. Everything works the same way except that we are by-passing the SOAP protocol.

By the way, the same idea can be extended to programs that have nothing to do with the web service.

For example, if you have a .NET program that needs to invoke a business function defined by a public method in a .NET DLL located on the same machine. As long as the method takes a typed data set as input and returns a typed data set as output, your program can invoke it using the DataSetServiceUtility library (you still need to add an entry to your program's configuration file to define the external business function, as described earlier in the article).

Disclaimer

The above ideas and the included code have not been tested/used in any real applications yet. The approach that I have described may be suitable only for internal use (the clients are other applications within the company, not internet users or customers). Although I intend to use only one instance of DataSetService on each server, there is nothing that prevents you from using multiple instances.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Xiangyang Liu 刘向阳



United States United States

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionWhy on earth would you want to expose functionalitiy like this? PinmemberRamon Smits6:55 27 Dec '05  
AnswerRe: Why on earth would you want to expose functionalitiy like this? PinmemberXiangyang Liu8:06 27 Dec '05  
GeneralRe: Why on earth would you want to expose functionalitiy like this? Pinmembersdfgasdfsfgsdfg8:54 27 Dec '05  
GeneralUpdate 2005/12/27 PinmemberXiangyang Liu5:53 27 Dec '05  
GeneralSoap services already loosely coupled enough, don't like the idea of making it even looser Pinmemberyc4king3:57 21 Dec '05  
GeneralRe: Soap services already loosely coupled enough, don't like the idea of making it even looser PinmemberXiangyang Liu4:48 21 Dec '05  
GeneralRe: Soap services already loosely coupled enough, don't like the idea of making it even looser Pinmemberbrackettm2:24 28 Dec '05  
GeneralRe: Soap services already loosely coupled enough, don't like the idea of making it even looser PinmemberXiangyang Liu10:55 28 Dec '05  
GeneralRe: Soap services already loosely coupled enough, don't like the idea of making it even looser Pinmembersdfgasdfsfgsdfg9:17 27 Dec '05  
GeneralRe: Soap services already loosely coupled enough, don't like the idea of making it even looser PinmemberXiangyang Liu11:39 27 Dec '05  
GeneralGets complicated in a team environment PinmemberArankun6:34 15 Dec '05  
GeneralRe: Gets complicated in a team environment PinmemberXiangyang Liu11:23 15 Dec '05  
GeneralSeperate AppDomains PinmemberGambachi2:43 9 Dec '05  
GeneralRe: Seperate AppDomains PinmemberXiangyang Liu2:59 9 Dec '05  
GeneralRe: Seperate AppDomains PinmemberGambachi6:19 11 Dec '05  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120517.1 | Last Updated 27 Dec 2005
Article Copyright 2005 by Xiangyang Liu 刘向阳
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid