Explore the Microsoft Application Blocks

Avoid coding infrastructure requirements by using the Microsoft Application Blocks.

asp:feature

TECHNOLOGIES: VS .NET | C# | VB .NET

ASP.NET VERSIONS: 1.0 | 1.1

 

Explore the Microsoft Application Blocks

Avoid coding infrastructure requirements by using the Microsoft Application Blocks.

 

By Brian Noyes

 

Most real-world applications share common requirements at the infrastructure level. Your apps need to access data, handle exceptions, access configuration data, and so on. Before the .NET Framework came along, a lot of people spent a lot of time writing little utility classes and subroutines for common coding tasks, such as parsing strings, doing type conversions, and manipulating paths. The classes in the .NET Framework save you from doing a lot of low-level coding for which you used to have to spin your own solutions.

 

But you still end up writing a lot of repetitive and mostly uninteresting code to address those infrastructure requirements because they exist at a higher level of abstraction than even the individual classes in the .NET Framework were designed to address. Microsoft has seen the need to address these infrastructure requirements, as well, and has begun providing mini-frameworks, called Application Blocks, to address common requirements most organizations have for their applications. The Application Blocks are quasi-products from Microsoft. They are developed and delivered as products by Microsoft and are even supported through tech support, but they are free, so you can't beat the price.

 

The first two Application Blocks were released last year and addressed data access and exception management. A new version of the Data Access Application Block (DAAB) has been released, as have a slew of new Application Blocks to add to the mix. In this article, I will introduce the Application Blocks as a whole, and I will dive deeper into two of the blocks: the DAAB and the User Interface Process (UIP) Application Block. In a series of follow-up articles online, I will discuss each of the other blocks in turn.

 

Pick from the Menu

I'll skip over the DAAB and UIP for now because I will cover them in more detail later. Let me quickly introduce you to the other selections on the menu before I cover today's specials.

 

First off, the Application Blocks all share some common goals and capabilities. The biggest goal is to make it so that you can write less code and have consistent and clean interfaces for satisfying the infrastructure requirements of your applications, allowing you to focus on designing and coding your core logic. Obviously, they need to be reliable and to perform well. To be truly reusable, they also need to be highly extensible - which the Application Blocks are.

 

Almost all the Application Blocks are designed to deliver a usable capability as soon as you download the code, but they have numerous extensibility points so you can use them as designed while adding to their capabilities without having to modify the code delivered by Microsoft. They also are configuration-driven, which means that extending their underlying capabilities often requires no new coding in the consuming application. So, for example, you can add a new publisher of exception information without changing any of the code that actually makes calls to the exception manager to publish. Each Application Block comes with full source code, documentation, and samples to help you get started using and extending them.

 

The Application Blocks I will be covering in future articles include:

  • The Exception Management Application Block, which includes capabilities for publishing exception context information to various forms of storage.
  • The Configuration Management Application Block, which allows you to read and write application-configuration data and settings to and from multiple forms of storage providers.
  • The Updater Application Block, which implements a pull mechanism to update applications over distributed networks easily and automatically.
  • The last three blocks are focused on Service Oriented Architecture (SOA) and allow you to make calls to services in an asynchronous manner and to cache and aggregate the data those services return.

 

Smart Data Access for the Masses

ADO.NET is a powerful and flexible object-oriented API for data access. However, for most business applications, the ADO.NET API is a little too fine-grained for concise, maintainable code. The DAAB gives you a simplified API for data access that minimizes the amount of repetitive code you need to write for a typical data-access layer. Version 2 of the DAAB was released recently and adds some great new capabilities to what was released in version 1 more than a year ago. The DAAB classes are designed to work exclusively with the SQL Server managed provider, but if you need to work with other databases, you could implement a custom version of the DAAB targeting other providers. An Oracle implementation based on version 1 of the DAAB is included with the Pet Shop 3 sample application.

 

The DAAB is composed of only two classes, the SqlHelper and SqlHelperParameterCache classes. SqlHelper has a bunch of overloaded ExecuteXXX methods (see Figure 1). The parameters for these methods include connection, command, and parameter information. The various overloads of each method give you a lot of options in terms of how the connections are constructed, commands are executed, and parameters are passed. Basically, the calling patterns of these methods minimize the amount of construction and population of individual objects that you need to do just to execute a single command against the database.

 

Method

Result

ExecuteDataset

Executes a query and returns a new DataSet object containing the result.

ExecuteReader

Executes a query and returns an open SqlDataReader ready for use in accessing the contained rows.

ExecuteNonQuery

Executes a query that is not expected to return any result set. The method returns the number of rows affected by the query. You can provide SqlParameters that are out or return value parameters and harvest those after completion of the method calls.

ExecuteScalar

Executes a query that is expected to return a single-row, single-column type of result set and returns that value as the return value from the function. This saves you from having to reach into a returned result set to extract a single value when you know that is all that will be returned.

ExecuteXmlReader

Executes a query that is expected to return an XML node set as the result set. The DAAB and ADO.NET wrap those return results in an XmlReader that you can use to iterate through the nodes.

FillDataset

Executes a query to add a table to an existing data set that you pass in as an argument. This allows you to construct multi-table data sets or work with strongly typed data sets using the DAAB.

UpdateDataset

Executes an update query to perform updates, insertions, and deletions based on a data set containing modified records.

CreateCommand

Automatically constructs a command object for performing updates by querying the database for the parameter set for a stored procedure.

Figure 1. These are the families of query methods of the SqlHelper class. Each of these families has multiple overloads to allow variations in the construction of the connection, command, and parameter information required to execute the query.

 

Version 2 of the DAAB includes several significant enhancements compared to what was in version 1. These include the abilities to work with strongly typed data sets, populate a data set with multiple tables, and update the database using data sets. There are also some additional discovery methods available for automatically creating commands, and there are some additional ExecuteXXXTypedParams methods that let you pass your parameters in the form of a populated DataRow. Using those methods, if you have parameters for a query already in the form of a DataRow, you don't need to push the parameters into individual SqlParameter objects but can just pass the DataRow containing the parameter values instead.

 

The SqlHelperParameterCache class primarily is used internally by the SqlHelper class, but it has several public methods that will allow you to discover and cache parameter sets to reduce the cost of repeatedly reconstructing those parameter sets. You need to be careful in the way you use these methods, though, because they can introduce a coupling between your data-access code and the underlying queries or stored procedures, based on the specific order of the parameters that are returned. Your code will be more maintainable and easier to understand if parameter matching is based on name rather than on the order in which the parameters appear, because then parameters can be inserted or removed from the underlying query without breaking the data-access code.

 

The download code for this article includes a sample application that uses the DAAB to perform some queries against a custom database that contains two tables: Files and Folders. See the download ReadMe.htm file for details on how to construct and populate this database on your machine. The FileSystemDataAccess sample application performs a bunch of different query results and presents them in a Windows user interface to let you experiment with performing queries and updates using the DAAB. For example, to use the DAAB to get all the files as a data set from the database using a stored procedure, and to name the table that is created in the data set, all that is required are two lines of code:

 

DataSet ds = SqlHelper.ExecuteDataset(

DataGlobals.ConnectionString,"GetFiles", null);

ds.Tables[0].TableName = "Files";

 

Tables that are created by the ExecuteDataset method use the default ADO.NET naming, so you will have to name them after they are returned if you desire. You also could pre-create the data set and pass it into the FillDataset method, which allows you to specify the table names as arguments. Using this latter method, you also can populate typed data sets and multi-table data sets.

 

The SqlHelper class handles construction of the connection object as well as opening it, creating a command object for the specified stored procedure, executing the query through a data adapter to fill a data set, closing the connection, and returning the result. If you were using raw ADO.NET, you would have to do all those things yourself for every query you made to the database. By using the DAAB, all those repetitive and error-prone actions are neatly encapsulated in the SqlHelper class, which allows you to write much less code and focus on the business logic of your application rather than data-query goo.

 

As of version 2 of the DAAB, you also can perform updates using data sets containing modified, inserted, or deleted rows. Figure 2 shows an example that uses the new CreateCommand method to construct the command objects for the updates automatically and then performs the updates with the UpdateDataset method. The code in Figure 2 also uses the CreateCommand method to construct the update commands using a stored procedure name and the names of the parameters to pass to that stored procedure. CreateCommand will locate those parameters and their types with a round-trip to the database and will pre-create the parameters collections for those commands. Then these parameter collections will be used for each update, insert, or delete operation that is required based on the current records in the data set that is passed to UpdateDataset.

 

public static void UpdateFiles(DataSet ds)

{

   // Create the connection

   SqlConnection conn =

      new SqlConnection(DataGlobals.ConnectionString);

   // Construct the update commands - all three are req'd

   SqlCommand insertCmd = SqlHelper.CreateCommand

       (conn,"AddNewFile","Name","Size","ParentFolderID");

   SqlCommand updateCmd = SqlHelper.CreateCommand

       (conn,"UpdateFileName","FileID", "Name");

   SqlCommand deleteCmd = SqlHelper.CreateCommand

       (conn,"DeleteFile","FileID");

   // perform the update

   try

   {

      SqlHelper.UpdateDataset

          (insertCmd, deleteCmd, updateCmd,ds,"Files");

   }

   finally

   {

      conn.Close();

   }

}

Figure 2. Like other data-access operations with the DAAB, performing an update with a data set requires minimal code that is easy to write and understand.

 

Decouple Your Presentation Tier

The UIP Application Block targets a common application requirement for presentation-tier applications - the need to implement workflow and state management between views, or individual forms, of your application. The UIP is designed at a much higher level of abstraction than the DAAB and constitutes a small presentation-tier framework unto itself. The driving force behind the UIP is that, for maintainability, you want to minimize coupling between the individual views (think forms) of your application.

 

Consider an application that needs to implement something along the lines of a shopping cart, quiz or questionnaire, or registration process. All these require that a navigation flow exist between individual views of the application, as well as state that gets shared between views and may be passed down into the business tier while the process is in progress, or at the end when it is complete. You may have a strictly linear flow or one with multiple paths and branching. The problem is that if you explicitly code individual views to know about every path into and out of their place in the process, along with all state-passing requirements for those transitions, you run into significant maintainability problems as the complexity of your process grows or when you want to add or remove steps in the process.

 

The UIP addresses this by implementing a model-view-controller pattern that completely decouples the individual views of your application, allowing you to add or remove steps in the process or factor individual views into multiple views without having to touch any of the other views' code.

To use the UIP in your applications, you basically need to do several things. First, you need to define the "use" case that composes the process you are implementing. By doing this, you can lay out the navigation graph that satisfies that "use" case, including the views that compose it and what the transitions are between views. Next, you need to implement a controller class. This is a class you derive from the ControllerBase class in the UIP. This class will contain the navigation methods called by the views in your application and will be the single point of contact for your individual views. Next, you need to design the views of your application and derive them from either WinFormView or WebFormView, depending on the type of application. Finally, you need to add a bunch of information to your config file to tell the UIP where to find everything it needs at run time.

 

The ControllerBase class contains a state bag that the views can place state values into so that they can be shared across views without having to pass that state as part of a view transition explicitly (see Figure 3). That state also can be persisted through a number of configurable storage providers, allowing you to resume the task wherever it was abandoned, at a future time.

 


Figure 3. The UIP lets individual views of a process be ignorant of one another. Each view just interacts with the controller, placing information in its state collection and calling methods on the controller that result in the UIP manager displaying the next appropriate view based on configuration data. The state can be persisted to different providers, just by changing config-file entries.

 

An example of what the code ends up looking like from the perspective of the view is shown in Figure 4. You access the controller as a protected member of that base class and cast it to the derived controller to access the specific navigation methods that you implement in that derived class. Views also store the ongoing results of the process in the state collection contained by the controller. Note that there are no references to other views, nor is there any explicit passing of state to another view.

 

public class WhatIsQuest : WebFormView

{

   // Details omitted...

   private QuizState GetQuizState()

   {

      return (QuizState)Controller.State["QuizState"];

   }

   private void btnNext_Click

       (object sender, System.EventArgs e)

   {

      GetQuizState().Quest = txtQuest.Text;

      OnlineQuizController ctrl =

          (OnlineQuizController)Controller;

      ctrl.MoveNext();

   }

   private void btnBack_Click

       (object sender, System.EventArgs e)

   {

      GetQuizState().Quest = txtQuest.Text;

      OnlineQuizController ctrl =

          (OnlineQuizController)Controller;

      ctrl.MovePrevious();

   }

   private void btnSelect_Click

       (object sender, System.EventArgs e)

   {

      GetQuizState().Quest = txtQuest.Text;

      OnlineQuizController ctrl =

          (OnlineQuizController)Controller;

      ctrl.BranchSelectQuest();

   }

}

Figure 4. Views in a UIP application simply call navigation methods on their controller class and store the results of their portion of the process in a state collection.

 

Methods in the derived controller class that you implement are usually as simple as setting the NavigateValue on the state object and calling Navigate on the base class, as shown here:

 

public void MoveNext()

{

      this.State.NavigateValue = "next";

     Navigate();

}

 

This results in the base class determining the corresponding view for the NavigateValue, based on which view was current and based on what the configuration settings for that NavigateValue are for that view in the config file. This effectively adds a layer of abstraction between what the view sees as a navigation command on the controller class and how the UIP manager interprets that command at run time, all based on config-file entries that can be changed and reorganized without requiring any changes to source code.

 

There is a lot of complexity going on under the covers with the UIP to give you a rich, flexible, configurable, and maintainable model for process-oriented user interfaces. The download code for this article includes an online quiz sample that demonstrates the steps for using the UIP. The questions in the quiz may look a bit familiar to fans of Monty Python. See the ReadMe.htm for more details of what the steps were to implement this sample using the UIP.

 

The sample code in this article is available for download.

 

Brian Noyes is a software architect with IDesign Inc. (http://www.idesign.net), a .NET-focused architecture and design consulting firm. Brian specializes in designing and building data-driven distributed Windows and Web applications. He has more than 12 years of experience in programming, engineering, and project management and is a contributing editor and writer for C#PRO, asp.netPRO, and other publications. Contact him at mailto:[email protected].

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish