XYAppServices: A Framework For .NET Business Applications

Copyright© 2005-2006 Xiangyang Liu (XYAppServices@verizon.net)

Table of Contents
  1. Introduction to XYAppServices   4. Built-in Business Service Functions
2. Application Framework 5. System Administration
3. Programming Guide 6. System Settings

Introduction

XYAppServices is a simple framework for .NET business applications. It allows you to write simple .NET code, called business service functions, to fulfill your business needs. The code you write in this framework will be able to interact with each other easily. Client programs invoke your code via web service calls.

Here are the main advantages of XYAppServices:

  1. It provides a simple and flexible framework for business applications.
  2. Complex interactions among different applications can be handled elegantly.
  3. It forces code reuse and simplifies application interfaces.
  4. Many common business services, such as database access, session management, event processing, are already built into the framework.
  5. No steep learning curve.

System Requirements

Note: Client programs do not have to be run on Windows machines.

Download & Installation

First, you need to download this software. After download, if you run the AppServicesInstall.vbs script, the following will be installed on your computer:

Copyright & License

Copyright of XYAppServices belongs to Xiangyang Liu. You can download and use the software free of charge on up to three server machines, there is no restriction for the number of client machines. For information on how to purchase the source code and other usage restrictions, please send e-mail to XYAppServices@verizon.net.

The Application Framework

The architecture

The framework is based on a special web service which is the communication bridge in XYAppServices. You deploy the special web service to one or more of your servers. The web service provides built-in business service functions such as database access, session management, e-mail, event notification and processing, etc.

If you want to add a new functionality to your server, all you have to do is write a .NET dll which contains at least one business service function and deploy the code to one of the servers, then you configure the business service function using the AdminForm.aspx page so that others can invoke the service you provide.

The special web service does not have to be modified to run your new code. Only simple configuration needs to be done by the administrator after the new dll is copied onto the server machine.

As you can see from the above picture, our special web service can be deployed on multiple servers. There is nothing to prevent you from deploying the special web service multiple times on a single server (you need to use a different virtual directory for each instance on the same server machine).

The special web service in XYAppServices

There are three methods in this web service, as described below:

Business service function

A business service function is some code that implements a unit of business work. It must satisfy the following conditions:

  1. It is written as a public method in a public class within a .NET dll. The public class must have a default constructor.
  2. Type of each input parameter of the method can be:
  3. The output of the method subjects to the same type restriction as the input.

The last condition seems to be a severe restriction on the usefulness of a business service function, however, since we can represent any complicated object as a string with XML format, there is no real limit in passing data to and from a business service function. For more discussions on this, please see the topic How to deal with complicated types in XYAppServices.

Remote business service function

In XYAppServices, you can deploy a business service function on Server A and configure it as a remote business service function on Server B. Client programs can invoke this service via Server B just like it was deployed locally on Server B. For more details see the topic How to configure and invoke a remote business service function.

A network of business servers

By using remote business service functions in XYAppServices, you can integrate a quite complicated network of servers that provide various business services to your clients. Here is one of the possible design of your business service network.

System security

XYAppServices is built with Microsoft .NET technology. The special web service in XYAppServices is hosted on Microsoft IIS web server. Therefore, you can use IIS to restrict access to the web service and hence restrict access to various business service functions running within the web service process. In addition, XYAppServices provides the following two security settings in its web.config file.

Note that by default there is no restriction on access to the special web service which means any client can invoke all of the business service functions deployed on the server. 

Programming Guide

How to write a new business service function in XYAppServices?

For example, your company stores employee information using a SQL server database. The database table EmployeeProfile contains employee information such as First Name, Last Name, Department, Grade, DOB, Gender, etc. Now you need to provide a service to another application that returns the list of employees from a selected department whose grade falls into a certain range.

You can write a simple business service function to accomplish this. The input parameters of your service should be: the name of the department (string), the lower grade limit (int), and the upper grade limit (int). The sql statement to get the records from the SQL server will be similar to the following:

select
    LastName + ', ' + FirstName as EmployeeName
from
    EmployeeProfile
where
    Department = @Department and
    Grade >= @LowerLimit and Grade >= @UpperLimit

Here is an example of your business service function:

public class MyClass
{
    public MyClass(){ }
    public string[ ] MyMethod(string sDepartment, int nLowerLimit, int UpperLimit)
    {
        string [ ] pOutputList;
         // ... get data from the database with the above sql query
        // ... construct the output string array
        return pOutputList;
    }
}

After compiling the code, you copy the dll to a folder on the server machine and configure it as a business service function. See the topic below for details.

How to deploy and configure a business service function in XYAppServices?

First of all, compile the code, let's say GetEmployeeList.dll is the name of the dll. Then copy the dll onto the server machine on which XYAppServices has been installed. Finally, use the AdminForm.aspx page to configure the new business service function.

After entering service name, file path, class name, and method name, click the "Update" button to save the configuration information. Now the new business service function is ready to be used by client programs.

Note: The service name string is case-sensitive and it is used internally as the key for the business service function.

How to invoke a business service function in XYAppServices?

The business service function GetEmployeeList we just deployed can be invoked easily by client applications via the SOAP protocol. First, we add a web reference to our special web service in the client application project. This step will generate a proxy class in our client application, say MyProxy. The following code in the client application will invoke the GetEmployeeList function we just built and deployed.

public string[ ] GetEmployeeList(string sDepartment, int nLowerLimit, int UpperLimit)
{
    MyProxy oProxy = new MyProxy( );
    oProxy.Url = "http://MyServer/ApplicationServices/AppSvc.asmx";
    object[ ] pInput = new object [3]{sDepartment, nLowerLimit, nUpperLimit};
    string[ ] pOutputList = (string[ ])oProxy.InvokeService("GetEmployeeList", pInput);
    return pOutputList;
}

Here is what happens when the above method is called:

  1. A new proxy object for our special web service is created.
  2. The URL of the proxy object is set.
  3. The three input values are packed into an object array.
  4. The InvokeService method of our special web service is called passing the service name "GetEmployeeList" and the input object array as parameters.
  5. Within our special web service, the GetEmployeeList.dll will be loaded according to configuration information stored on the server and the method MyMethod in MyClass will be called to do the work. The returned employee name list (a string array) is sent back to the client.
  6. The returned object is casted into a string array.

The InvokeService method in our special web service takes two parameters, one is a service name string, the other is an object array which should contain input values to be fed to the corresponding business service function that will be loaded and run within the web service process. The return value is an object which should be casted to an appropriate type.

How to configure and invoke a remote business service function?

Suppose a business service function MyFunction is deployed on Server A. Now on Server B, you can configure a remote business service function with exactly the same name using the AdminForm.aspx page, like the following:

The service url in the above is the url for the special web service on Server A.

The best part is, invoking a remote business service function from a client is exactly the same as invoking a locally deployed business service function. When a client program calls the remote business service function on Server B, the framework will detect that this is a remote service, it will direct the request to Server A and return the result from Server A to Server B and then to the client, automatically.

How to deal with complicated types in XYAppServices?

As said earlier, the input and output of business service functions are restricted to

That means you can use almost any kind of "data structure" as an input parameter in business service functions. 

For example, you want to use an "employee" data type as input parameter for your business service function GetEmployeeAge. The "employee" type has the following fields: EmployeeID (int), FirstName (string), LastName (string), DOB (datetime), and Gender ('M' or 'F'). Your business service function can be written and used as below:

int GetEmployeeAge(object[ ] pEmployee)
{
    DateTime oNow = DateTime.Now;
    DateTime oDOB = Convert.ToDateTime(pEmployee[3]);
    int nYears = ((oNow-oDOB).Days/365);
    int nAge = oDOB.AddYears(nYears)>oNow?(nYears-1):nYears;
    return nAge;
}
...
object[ ] pEmployee = new object [5]{101, "John", "Doe", Convert.ToDateTime("11/12/1969"), 'M'};
int nAge = GetEmployeeAge(pEmployee);
...

Obviously, you cannot use just any class as the type of input or output for business service functions. This is a limitation of XYAppServices, but we gain some flexibility with the loss of type safety. In order to achieve type safety, you can provide wrapper classes on the client side that take class types as input and output. The wrapper class will translate the input from the client, call your business service function, and translate the output from the server. This is exactly what is done in AppClient.dll and AppClientLib.dll.

Another solution is use XML string (string that represents an XML document) as input and output. In this case, you can either parse the XML document or convert the XML string to and from typed data set. 

Built-in Business Service Functions

The two .NET dlls, AppClient.dll and AppClientLib.dll, can be used by client programs to access the built-in business service functions in XYAppServices. AppClient.dll is for regular client programs, it accesses the built-in services via the SOAP protocol. AppClientLib.dll is for programs that will be running within the process of the special web service (programs that implement custom business service functions), it loads and invokes internal assemblies in XYAppServices.

Both AppClient.dll and AppClientLib.dll have only one class named Tools . The interface of the Tools class consists of a set of public static functions. The signatures of these static functions in the two assemblies are the same except that functions in AppClient.dll takes one additional parameter, sServiceURL, which is the url of the special web service in XYAppServices.

You can also use the public static method InvokeService in AppClient.dll to call any other business service function. The following topics provide more details on the built-in business service functions and how to invoke them.

Session management

XYAppServices provides easy and flexible session management. In XYAppServices a session is uniquely identified by a session id string, it contains a set of session data items. Each session is associated with a timeout value (in minutes). Within a session a session data item is uniquely identified by a key string. Session data is stored in memory and also persisted into the Sessions.data file on the server. If the web server is restarted or the machine is rebooted, no session data will be lost.

Each session has an internal timestamp which represents the last time it was accessed. When a session is accessed, its internal timestamp will be updated. If a session has not been accessed for the number of minutes specified in its timeout value, it will expire. Expired sessions will be removed automatically.

Different client programs can share the same session by using the same session id, this is true even if the client programs are running on different machines.

Create Session To create a new session, you call the methods CreateSession or SetSessionTimeout, passing a session id string and an integer timeout value (timeout is measured in minutes). These two methods return a boolean flag to indicate success or failure. The SetSessionTimeout method can also be used to change the timeout setting of an existing session. If a session with given id already exists and has timed out, the return value will be false. The return value will also be false if the session id string is null or empty or the timeout value is not positive.

Set Session Data The SetSessionData and SetSessionDataArray methods are used to store data into the session. You need to pass a session id string and a key string, in addition to the data value(s) you want to store. The first method takes an object as session data value, the second takes an array of objects. Session data value must be

These two methods will also create a new session if no session exists with the given session id string. The newly created session will have the default timeout value which is 30 minutes. The return value is a boolean flag to indicate success or failure.

Get Session Data The GetSessionData and GetSessionDataArray methods retrieve session data previously stored in session. You need to pass a session id string and a key string. If no data can be found or the session has expired, then the return value will be null.

Remove Session The RemoveSession method will remove the session with the given session id.

Here is some sample code using the built-in session functions.

// create a session that will expire in 10 minutes
string sURL = "http://MyServer/ApplicationSerivices/AppSvc.asmx";
string sID = Guid.NewGuid( ).ToString( );
AppClient.Tools.CreateSession(sURL, sID, 10);
// store an array to session
object[ ] pInput = new object[3]{"test 123", 101, 3.14};
AppClient.Tools.SetSessionData(sURL, sID, "Array", pInput);
// retrieve the array from session
object[ ] pOutput = (object [ ])AppClient.Tools.GetSessionData(sURL, sID, "Array");
// remove the session
AppClient.Tools.RemoveSession(sURL, sID);

Application settings

You can store and retrieve application settings in XYAppServices. An application setting value is a string uniquely identified by an app name string and a key string.

The SetAppSetting method stores an application setting value in XYAppServices and the GetAppSetting method retrieves a previously stored setting value. Once you called SetAppSetting method to store a value, it will be there even after you restart the web server or reboot the machine. It is possible for different programs to share the same app setting as long as you pass the same app name string and key string when calling the GetAppSetting method.

For each server, you can write a simple program that stores all the intial settings for all client applications by calling the SetAppSetting method. Your client applications will call the GetAppSetting method to retrieve all the settings it needs from the server.

Here is sample code for using application settings.

// store max number of users
int nMaxUsers = 200;
AppClient.Tools.SetAppSetting(sServiceURL, "MyAppName", "MaxUsers", nMaxUsers.ToString( ));
...
// retrieve max number of users
string sValue = AppClient.Tools.GetAppSetting(sServiceURL, "MyAppName", "MaxUsers");
int nMaxUsers = Convert.ToInt32(sValue);

Database access

XYAppServices  provides three different methods to access relational databases: ExecuteSQL, ExecuteOleDb, and ExecuteOdbc.

The above three methods have identical parameters. The first parameter is a url string that specifies which instance of the  XYAppServices server you are going to use. If your client program runs within the process of the special web service, then you can use AppClientLib.dll instead of AppClient.dll in which case no url string is needed.

The second parameter, sConnection , is a database connection string whose format depends on the method you are calling. In general, it specifies data source, user id, and password. The third parameter, sSQL, is the actual sql statement, it can be a "select ... from ..." statement or a call to a stored procedure.

The return value of these three methods is a .NET DataSet object. If the sql statement produces any record set as output, it will be contained in the returned DataSet object. Otherwise the returned DataSet object will be empty. In case of database error, an exception will be thrown.

The following is some sample code for database access using built-in business service functions.

// accessing ODBC datasource
string sConnect="DSN=MyData;UID=Me;PWD=test123";
string sSQL = "select * from mytable";
DataSet oDS = AppClient.Tools.ExecuteOdbc(sServiceURL, sConnect, sSQL);
// print output to console
System.Console.Out.WriteLine(oDS.GetXml( ));

Send e-mail

XYAppServices allows you to send e-mails via SMTP. If the SMTP service is not enabled on the server machine, you need to specify the SMTPServer setting in the web.config file. Receiving e-mail is not supported.

The SendEMail method takes the following parameters:

  1. sFrom: Sender e-mail address.
  2. sTo: Semi-colon delimited e-mail addresses.
  3. sCc: Semi-colon delimited e-mail addresses, can be null or empty.
  4. sBcc: Semi-colon delimited e-mail addresses, can be null or empty.
  5. sReplyTo: Address for replying e-mail, can be null or empty.
  6. sSubject: The subject line.
  7. sBody: The e-mail body.
  8. sAttachment: Semi-colon delimited file paths, can be null or empty.
  9. bHTML: If true, the e-mail body is treated as HTML, otherwise it is treated as plain text.

The SendMassEMail method is used to send e-mail to many people, it will send the e-mail in separate messages if there are too many recipients. No recipient e-mail address will be visible in the received e-mail. The method takes the following parameters:

  1. sFrom: Sender e-mail address.
  2. sTo: Semi-colon delimited e-mail addresses, can be null or empty.
  3. pBccList: A string array of recipient e-mail addresses (no address will be visible in received e-mail).
  4. sSubject: The subject line.
  5. sBody: The e-mail body.
  6. sAttachment: Semi-colon delimited file paths, can be null or empty.
  7. bHTML: If true, the e-mail body is treated as HTML, otherwise it is treated as plain text.
  8. nBatchSize: The e-mail will be sent to at most nBatchSize recipients at a time.

The code below demonstrates the use of SendEMail and SendMassEMail. The first parameter is the service url which is not needed if AppClientLib.dll is used instead of AppClient.dll. Both calls send e-mails to the same group of 10 people, however the second call will generate 4 different messages because the batch size is 3.

// send e-mail
string sTo = "a0@b.c;a1@b.c;a2@b.c;a3@b.c;a4@b.c;a5@b.c;a6@b.c;a7@b.c;a8@b.c;a9@b.c";
AppClient.Tools.SendEMail(sServiceURL,"me@Test.com,sTo,null,null,null,"Test","This is a test",null,false);

// send mass e-mail
string[ ] pToList = new string [10]{"a0@b.c","a1@b.c","a2@b.c","a3@b.c","a4@b.c","a5@b.c","a6@b.c","a7@b.c","a8@b.c","a9@b.c"};
AppClient.Tools.SendMassEMail(sServiceURL,"me@Test.com",null,pToList,"Test","This is a test",null,false,3);

Event notification and processing

XYAppServices supports basic event notification and processing. It is done in the following fashion.

  1. First, an event type is registered by invoking the RegisterEvent business service function.
  2. Then one or more event handlers are registered for this even type by invoking the RegisterEventHandler business service function. An event handler can be code in a .NET dll that will be loaded and run within the process of the special web service or it can be a remote business service function that will to be called via SOAP protocol. 
  3. Client programs post event for the registered event type by invoking the PostEvent business service function.
  4. A posted event will be processed by the system in several background threads, all current handlers for this event type will be called.

Note: The system automatically stores event information in file Events.data in the AppData subfolder. If the web server is restarted or the machine is rebooted, no event information will be lost.

RegisterEvent This function takes the following 4 parameters:

  1. sEventName: The name string of the event type.
  2. sFailureEMail: The e-mail address that a notification message will be sent to when an error occurs during the processing of the event.
  3. nExpirationMinutes: The number of minutes from the time an event is posted to the time the event expires. An expired event will be removed from the internal queue and discarded.
  4. nDelayMinutes: The number of minutes from the time an event is posted to the time the event can be processed. For example, if this value is 5, then an event of this type will not be processed by the system until 5 minutes after the event is posted.

An event type can be unregistered by calling the UnRegisterEvent business service function passing the event name.

RegisterEventHandler This function takes the following 8 parameters:

  1. sEventName: The name string of the event type.
  2. sHandlerName: The name string of the event handler.
  3. IsRemoteHandler: A boolean flag indicating whether the handler is implemented in a remote business service function or in a .NET dll locally.
  4. sFilePath: The file path of the .NET dll that implements the handler (only applies when this is not a remote handler).
  5. sClassName: The class name for the handler (only applies when this is not a remote handler).
  6. sMethodName: The method name for the handler (only applies when this is not a remote handler).
  7. sServiceName: The name for the remote business service function for the handler (only applies when this is a remote handler).
  8. sServiceURL: The url of the remote business service function for the handler (only applies when this is a remote handler).

An event handler can be unregistered by calling the UnRegisterEventHandler business service function passing the event name and the handler name.

PostEvent This function is called by client programs to post events. It takes the following two parameters.

  1. sEventName: The name string of the event type.
  2. pEventArgs: An array of objects that will be passed to each of the event hanlders when the current event is being processed.

Here is some sample code demonstrating event processing in XYAppServices.

// register event type "MyEvent"
// this type of event will expire in 60 minutes
// no e-mail will be sent when processing error occurs

AppClient.Tools.RegisterEvent(sServiceURL, "MyEvent", "", 60, 0);
// register handler for event type "MyEvent"
// the handler is implemented in method MyMethod of class MyClass in MyHandler.dll

AppClient.Tools.RegisterEventHandler(sServiceURL,"MyEvent","MyHanlder",false,"MyHandler.dll","MyClass","MyMethod","","");
// post an event of type "MyEvent", passing a string and an integer as event arguments
AppClient.Tools.PostEvent(sServiceURL,"MyEvent",new object[2]{"Test", 123});

Note: The same restrictions on input parameters for business service functions apply to event handlers except that event handlers do not return any value.

System Administration

IP restirction

The first thing an administrator should do is modify the settings AdminIPList and ClientIPList in the web.config file (see the topic system settings for details) so that only authorized clients can access and change the business service functions deployed on the server.

As an administrator, you can add, update, or delete a business service function. You can also view system session data. If you want to view and change the built-in business service functions, you need to set the ViewSystemService value to true in the web.config file.

AdminForm.aspx

The system administrator uses the AdminForm.aspx page to add, update, and delete business service functions.

The "Service List" dropdown on this page contains business service functions deployed on this server. You can select any item from the list, modify it, and then click the "Update" button. Your changes will be saved. If you click the delete button, then the selected business service function will be deleted.

To add a new business service function, assuming you already copied the related dlls onto the server, you need to select the first item "Add new service" from the dropdown, then enter other fields on the screen and click the "Update" button. If you check the "Is Remote Service" box, the screen will change appearance to allow you to add a remote business service function.

If the app setting "ViewSystemService" is set to true in the web.config file, then the "Service List" dropdown will include all built-in business service functions. The name of a built-in business service function begins with character '_'. It is possible for you to add, update, and delete a built-in business service function. Be very careful when you do this.

The "Load Data" button loads system data from files in the AppData subfolder. The "Store Data" button stores system data to files in the AppData subfolder. If you make backup copies of the files, you can always restore system data by replacing the files and clicking the "Load Data" button.

SessionBrowser.aspx

From the AdminForm.aspx page, you can open the SessionBrowser.aspx page. This page displays all session data in XYAppServices.

The "Session List" dropdown contains the id strings of all current sessions. After you selected a session from this list, the "Key List" dropdown will be populated with all key strings within the selected session. If you select a key from this dropdown, the corresponding value for the selected key will be displayed in the value box.

The list of sessions in the "Session List" dropdown is just a snapshot. While this page is open, new sessions won't be added to dropdown and expired sessions won't be removed either.

Please note that some of the sessions you see on this page are used by the system itself.

Server log (trace)

Log (trace) files will be created automatically in the Log subfolder. Each day there will be a new log file. If the current log file becomes too large, it will be closed and a new one will be opened. Each entry in the log file contains a timestamp as well as a guid string to identify the current thread.

The TraceLevel setting controls how much information is written to the log file. The TraceDelete setting controls how often old log files are deleted. Please see details about these two settings in the next topic.

System Settings

All system settings are defined in the [AppSettings] section of the web.config file on the server. Here is the list of all system settings in alphabetic order.

AdminIPList This setting controls who can access the AdminForm.aspx page and the SessionBrowser.aspx page. It is a list of semi-colon delimited (partial) ip addresses. By default, anyone can access the admin pages.

AppDataFolder This is the full path of the folder to store system data. The AppData subfolder will be used by the system if no value is specified.

ClientIPList This setting controls who can access XYAppServices. It is a list of semi-colon delimited (partial) ip addresses. By default, anyone can access XYAppServices.

EventEMailSender This is the e-mail address that is used as the sender for any event related e-mails.

EventEMailServiceURL This is the url for an instance of XYAppServices that is responsible for sending event related e-mails.

EventProcessingThreads The number of background threads to process posted events. The default is 5.

SMTPServer This is the name or the ip address of the SMTP server to be used by the built-in e-mail functions.

TraceDelete This is the number of days before old log (trace) files are deleted. Value 0 means old log files will not be deleted. The default value is 7 if no value is specified.

TraceFolder This is the full path of the folder to store log (trace) files. The Log subfolder will be used by the system if no value is specified.

TraceLevel This value should be one of the following: 0, 10, 20, 30, 40, while 0 means no tracing and 40 means the most detailed tracing. The default value is 40 if no value is specified.

ViewSystemService If this value is true, then administrators can view (and update) all built-in business service functions on the AdminForm.aspx page. Otherwise the built-in functions will be hidden.