In the first article, a basic outline of N-Tier development was discussed. In examining what can be done with .Net the topic shifted to the Data Tier as a starting point. A debugging view of the component for a Non-Transactional method call was made to return a DataSet to the calling function for the remainder of that part of the series.
This installment in the series will begin with how to maintain a transaction from the business faade layer to the database through a Web Service and will examine how to physically separate the business and data access layer from the presentation and data tiers.
Faade or Not Faade
A Faade is an extension of the business layer that further divides business functionality into organized units. For instance if an application has several entity access levels such as Customers, Employees, Administrators, Salesman, and Executives, then each entity may have its own level of needed functionality. Some of these entities may have the partially the same functionality. A faade allows abstraction of business functionality and allows encapsulation of functionality changes. Examine Figure 1 and notice that there is a main business component and three faades. Each faade exposes different functionality from the same business component to different sections of web pages representing different entities. While HR may need all of the functionality on the business tier, the Customer does not, nor does the Sales Person.
Figure 1.

This design concept has both good and bad points.
The good sides are:
Only exposes the methods needed to a consumer thereby increasing security and isolating the client from business logic changes.
Allows cutover from stubbed methods to actual data during the development phase with seamless integration to the presentation tier.
Much more granular approach to design.
The bad sides are:
- Increased development time
- Increased maintenance
For simplicity this article does not use a faade, but some definition needed to be given as a faade is a viable area where Web Services can be deployed. This proof of concept must have a root point where a transaction begins. The transaction context for this article begins in the HumanResource and the InsertTest Web Service projects found in the zip file. These will act as simple business components for demonstration purposes.
Currently with the release of Visual Studio .Net Web Services support transactions as long as the service is the root of the transaction. In the next release going beta in May, Microsoft is giving full transactional support that will allow a Web Service to be enlisted in a transaction from a higher context than its own.
The proof of concept Web Service project is named InsertTest for this example. It contains a service named Insert.asmx that has one transactional method named TransTester. Examine Listing 1 for an overview of the method.
Listing 1
[WebMethod(TransactionOption=TransactionOption.Required)]
public void TransTester()
{
ExecTransaction oExec = new ExecTransaction();
object[] oValues = new object[] {-1,1,2,3,4,5,6,7,8,9,10};
try
{
oExec.Insert("usp_InsertTest",oValues);
//purposely cause a fatal error after the first insert
//with a divide by zero error
int h = 0;
int i = 20/h;
oExec.Insert("usp_InsertTest",oValues);
ContextUtil.SetComplete();
}
catch(Exception oException)
{
if (!EventLog.SourceExists(APPLICATION_NAME))
{
EventLog.CreateEventSource(APPLICATION_NAME, "Application");
}
EventLog.WriteEntry(oException.Source, oException.Message,
EventLogEntryType.Error );
ContextUtil.SetAbort();
}
}
The [WebMethod(TransactionOption=TransactionOptionRequired)] creates a new transaction for the method that can be completed or aborted insuring that the data manipulation is atomic in nature. An instance of the ExecTransaction class from the TVanover.DDL is created and instantiated to make two inserts within the context of this new transaction. The first Insert is executed and an exception is generated before the second execution. The catch block records the error information to the application log and aborts the transaction, there by rolling back the first insert.
Listing 2 illustrates the ExecTransaction.Insert method.
Listing 2
[ AutoComplete( true ) ]
public void Insert(string StoredProc, params object[] oParameters)
{
SqlCommand oCmd = DataAdapter.GetParameters(StoredProc, oParameters);
try
{
oCmd.ExecuteNonQuery();
}
catch(Exception oException)
{
if (!EventLog.SourceExists(APPLICATION_NAME))
{
EventLog.CreateEventSource(APPLICATION_NAME, "Application");
}
EventLog.WriteEntry(oException.Source, oException.Message,
EventLogEntryType.Error);
}
finally
{
oCmd.Connection.Close();
}
}
This method has an the [AutoComplete( true) ] attribute set that is a member of System.EnterpriseServices . In order to use this functionality the parent class of the method must inherit ServicedComponent as shown in Listing 3.
Listing 3
[Guid("2B507812-A947-4214-AEAC-A4DFA97939DC")]
[ComVisible(true)]
[Transaction(TransactionOption.Supported)]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[ObjectPooling(true,5,100)]
public class ExecTransaction : ServicedComponent
The ExecTransaction class has other attributes set that include the following purposes:
[Guid("2B507812-A947-4214-AEAC-A4DFA97939DC")]
- Specifies a Globally Unique Identifier. This is used for COM Interoperability. It also prevents COM versioning on each compile. By design the CLR will keep multiple versions of a library compiled. This attribute eliminates this behavior where only one version is needed.
[ComVisible(true)]
- Specifies whether or not the class interface is visible in Component Services (COM).
[Transaction(TransactionOption.Supported)]
- This marks the class to share in a transaction if one exists.
[ClassInterface(ClassInterfaceType.AutoDispatch)]
- This is the default setting for the ClassInterfaceType attribute, exposes a dispinterface and supports late binding. This also allows versioning should it be desired. The ClassInterfaceType.AutoDual enumeration could be used alternatively, but Microsoft strongly recommends not using this ClassInterfaceType.
[ObjectPooling(true,5,100)]
- This generates an instance of the ObjectPoolingAttribute class, sets object pooling to true, sets a min and max pool size for the object.
The Insert method calls the DataAdapter.GetParameters method to instantiate and populate a SqlCommand object with a SqlConnection that is enlisted in a transaction. Notice in Listing 4 that the Connections.Connection is passing true to the bool parameter.
Listing 4
public static SqlCommand GetParameters(string storedProc,
params object[] oParameters)
{
SqlCommand oCmd = new SqlCommand(storedProc);
try
{
//this method requires parameters and will throw an
//exception if called without them
if(oParameters == null)
{
throw new ArgumentNullException(NULL_PARAMETER_ERROR);
}
else
{
DataTable oTable = ParameterCache.dsParameters.Tables[storedProc];
int iParameters = 0;
oCmd.CommandType = CommandType.StoredProcedure;
//write the parameters collection based on
//the cache and values sent in
foreach(DataRow oDr in oTable.Rows)
{
oCmd.Parameters.Add(CreateParameter(Convert.ToString(oDr[PARAMETER_NAME]), Convert.ToInt32(oDr[PARAMETER_DIRECTION]),
oParameters[iParameters]));
iParameters++;
}
//Add a return parameter
oCmd.Parameters.Add(CreateParameter());
oCmd.Connection = Connections.Connection(true);
}
}
catch(Exception oException)
{
if (!EventLog.SourceExists(APPLICATION_NAME))
{
EventLog.CreateEventSource(APPLICATION_NAME,"Application");
}
EventLog.WriteEntry(oException.Source, oException.Message,
EventLogEntryType.Error );
}
return oCmd;
}
The HumanResource Web Service project is part of the proof of concept application and has Update, Insert, Delete, and Search methods defined. Personnel.asmx and PersonnelAcc.asmx are almost identical services within this project with exception that the PersonnelAcc.asmx is designed to work with an Access based DataLayer database also provided in the zip file.
In order to have a physically separated middle tier there are two paths that can be taken. The first is to use remoting through the System.Runtime.Remoting namespace and using either TCP ports or HTTP ports to bind a service. The second is using a Web Service over HTTP. It should be noted that remoting over TCP provides faster access, but that many firewalls do not allow binary data streams, where as HTTP generally passes through most firewalls easily as it uses SOAP. It should also be noted that in using a Web Service to provide this functionality abstracts the developer from having to do much of he work associated with using remoting. For the example project a Web Service seemed to be the logical choice as many things are done for the developer automatically.
There are two ways the Web Service metadata (wsdl) can be imported into a project to allow remote use. The first is simply setting a web reference to the project or service and allowing the IDE to generate the wsdl. The second is more in line to larger projects that involve many developers and is the one that the rest of the article focuses on.
Wsdl.exe is a command line utility that allows the creation of a proxy class much like was done in traditional COM. In COM, an application proxy could be generated from inside the Com Services MMC that produced an .msi file to install to other machines in a network that gave a reference to the application server where the COM application resides.
To generate this proxy from the command line the following syntax is used from the Visual Studio .Net Command Prompt tool:
C:\>wsdl /out:Personnel.cs Http://Developer/HumanResource/Personnel.asmx
A .cs file is generated that points to a machine name of Developer, Web directory HumanResource, and the Personnel Web Service. This Personnel.cs file was written to the C:\ directory and can be added to a web project or winforms project to access the methods. Listing 5 shows the constructor that is used to point a reference to where the Web Service resides. This Url is statically set, but can be dynamically set as well allowing access to the Web.Config or App.Config file to access the <appsettings> section to determine the url by using the System.Configuration namespace.
Listing 5
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Web.Services.WebServiceBindingAttribute(Name="PersonnelSoap", Namespace=http://tempuri.org/)]
public class Personnel
:System.Web.Services.Protocols.SoapHttpClientProtocol
{
public Personnel()
{
this.Url = http://developer/HumanResource/Personnel.asmx;
}
There are 3 methods generated for each method on the actual Personnel.asmx service. The first is a synchronous call to execute the method, the second is an asynchronous call , and the third is a asynchronous callback. The interesting thing about this is each of these methods on the Personnel.asmx service is now given functionality that was only previously available to queued components in COM. In calling a queued component in COM+ asynchronous processing was possible for an application. Listing 6 Illustrates the AddEmployee method and its new counterparts.
Listing 6
[System.Web.Services.Protocols.SoapDocumentMethodAttribute(
http://tempuri.org/AddEmployee,
RequestNamespace="http://tempuri.org/", ResponseNamespace="http://tempuri.org/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public void AddEmployee(string firstName, string middleInitial,
string lastName, string socialSecurity)
{
this.Invoke("AddEmployee", new object[] {
firstName,
middleInitial,
lastName,
socialSecurity});
}
public System.IAsyncResult BeginAddEmployee(string firstName,
string middleInitial, string lastName,
string socialSecurity, System.AsyncCallback callback,
object asyncState)
{
return this.BeginInvoke("AddEmployee", new object[] {
firstName,
middleInitial,
lastName,
socialSecurity}, callback, asyncState);
}
public void EndAddEmployee(System.IAsyncResult asyncResult)
{
this.EndInvoke(asyncResult);
}
In conclusion Web Services offer a possible solution to a middle tier layer for either a faade or business layer in .Net. There is some transactional support that can enlist other components as long as the Web Service is the beginning of the transaction context. If an exception is generated from the database up to the Insert method the transaction is aborted or rolled back. In the example provided one insert is done at the business level and an error is generated causing the transaction to be rolled back. A similar error could have been generated at a lower level for the same results to occur. Also there is now the benefit of having asynchronous processing on a method basis that is much more simple than creating a queued component in some instances.
The next installment of the article will get into the UI in the proof of concept Employees Web Application and will show several OOP features that can be used in Web Development that were not previously available in .asp. It will also demonstrate hooking the Web Service methods into usable code for execution through the middle tier to the database and the asynchronous calls previously mentioned.
Join the conversation! Your thoughts help the community grow.