October 03, 2008 12:10 AM

Data Application Tricks

Performance, Reliability, and Accuracy
DevConnections
Rating: (0)

CoverStory

LANGUAGES:C# | VB.NET

ASP.NETVERSIONS: 3.5

 

Data Application Tricks

Performance, Reliability, and Accuracy

 

 

Data-driven applications are becoming more common thesedays, and the need for data at a user?s fingertips is more prevalent. Thesetypes of systems have different requirements, relying more on performance,reliability, and data accuracy than look and feel (and sometimes functionality).Although each application has different levels of criticality, the goal is thesame: provide data at a user?s fingertips.

 

Sometimes systems rely on certain sets of data more thanothers. For instance, a customer portal starts out with a customer, then drillsdown to related data from that central focal point. A sales engine relies moreon the sales data, then drills down into demographic information.

 

This article provides tips and tricks for more of thedata-centric applications, where data is a key component of the day-to-daybusiness. The accompanying sample application is a customer portal, driven bythe need to access customer data, along with the related products they purchaseand the orders they make (see end of article for download details).

 

Page Architecture

ASP.NET uses a class that represents an ASP.NET page; whencreating a new page, it has both markup and a code-behind class, which islinked together to create the output the user sees. The code-behind class is apublic class that inherits from System.Web.UI.Page, the base class for ASP.NETpages. This class contains the plumbing to process requests for ASP.NET pages.

 

To reference the page, the System.Web.UI.Control baseclass (from which even the Page class inherits) has a reference to the Page inwhich the control is defined. This reference points to the code-behind class ofthe page currently executing.

 

Remember that ASPX code-behind classes are required toinherit from System.Web.UI.Page indirectly, not directly. What this means isthat another level of inheritance can be inserted between the Page class andthe ASPX code-behind class. This is a very useful technique, because it allowsthe reuse of portions of code throughout the application, similar to theSingleton pattern. Let?s look at an example of this; Figure 1 shows a custompage class developed for a Customer Portal sample I?m developing. This custompage class defines useful events, properties, and methods that are beneficialto the application.

 

public class CustomerPortalPageBase : PageBase

{

 privateCustomerPortalDAL.CustomersRow _selectedCustomer = null;

 private Guid_selectedCustomerKey = Guid.Empty;

 

 public event EventHandlerSelectedCustomerChanged;

 

 public boolHasSelectedCustomer

 {

   get { return(this.SelectedCustomerKey != Guid.Empty); }

 }

 

 public GuidSelectedCustomerKey

 {

   get

   {

     if (_selectedCustomerKey == Guid.Empty)

     {

object value = this.Services.Caching.Get(

      this.Services.Caching.GetSafeKey(

      "SelectedCustomerKey"));

       if (value != null)

         _selectedCustomerKey= new

      Guid(value.ToString());

     }

     return_selectedCustomerKey;

   }

   set

   {

     if(_selectedCustomerKey != value)

     {

       _selectedCustomerKey= value;

       this.Services.Caching.Add(

      this.Services.Caching.GetSafeKey(

"SelectedCustomerKey"), _selectedCustomerKey);

       this.OnSelectedCustomerChanged(EventArgs.Empty);

     }

   }

 }

 

 publicCustomerPortalDAL.CustomersRow GetSelectedCustomer()

 {

   if (_selectedCustomer== null &&

      this.SelectedCustomerKey != Guid.Empty)

   {

     CustomersBAL bal = new CustomersBAL();

     _selectedCustomer =bal.GetByKey(this.SelectedCustomerKey);

   }

 

   return_selectedCustomer;

 }

 

 protected virtual voidOnSelectedCustomerChanged(EventArgs e)

 {

   if(SelectedCustomerChanged != null)

     SelectedCustomerChanged(this,e);

 }

}

Figure 1: A custompage class (CustomerPortalPageBase).

 

Now all the pages in the application can implement thiscustom page class. This won?t hurt any of your functionality becauseCustomerPortalPageBase still inherits from Page; it simply adds another levelof inheritance.

 

This approach has similarities to the Singleton pattern inthe respect that the properties and methods defined in the page class areavailable throughout the application, because controls and user controlsmaintain a reference to the page class, and can cast its Page property (whichreturns by default a reference to System.Web.UI.Page) to typeCustomerPortalPageBase.

 

Page/User Control Communication

In some situations, the base page class needs tocommunicate with the user control class on a level that the previous setupdoesn?t support. Although a page has all its children in the Controlscollection, it may be better to store a separate reference to a specific subsetof controls that have a special importance in the application.

 

For instance, the Web part manager knows everything aboutthe Web part zones that are on the same page. This happens because the Web partzones ?register? with the Web part manager on initialization. This is made easybecause of the ASP.NET lifecycle, and through a handy property on the Pageclass, named Items. The Items property returns a dictionary of items createdevery time the page runs. It?s simply an object dictionary that stores anythingthe child controls of the page want it to. The WebPartManager class stores areference of itself in this collection, which each Web part zone knows aboutand uses to register itself with the WebPartManager.

 

The process I use is very similar, but doesn?t make use ofthe Items property. Instead, the next example uses user control classes (whichare controls and can perform the same setup) to register themselves with thepage using interfaces at both the page and user control level.

 

The example I use is a portal page that representsinformation about the user. This portal page is comprised of customer ?widgets?that represent different information about the customer. These widgets willregister themselves with any page that implements the following interface:

 

public interface ICustomerWidgetContainer

{

 voidRegisterControl(ICustomerWidget control);

}

 

By having the widgets register themselves with the page,the application saves the processing time it takes to recursively loop throughthe control collection. To make use of this, the ASP.NET page that contains thewidgets adds these controls to an internal collection that it can make use oflater. In the process, it would be best to register the widgets atinitialization time, so they could be made use of early enough in the lifecycle(see Figure 2).

 

public partial class CustomerDetailsPage :CustomerPortalPageBase,

 ICustomerWidgetContainer

{

 private List<ICustomerWidget>_customerWidgetControls = null;

 

 protected List<ICustomerWidget>CustomerWidgetControls

 {

   get

   {

     if(_customerWidgetControls == null)

      _customerWidgetControls =

new List<ICustomerWidget>();

     return_customerWidgetControls;

   }

 }

 

 public voidRegisterControl(ICustomerWidget control)

 {

   this.CustomerWidgetControls.Add(control);

 }

}

Figure 2: Customerwidget container consumer.

 

What can register itself as a widget could be anything, aslong as it?s a control. For simplicity, user controls were selected because ofthe ease of designing them, in addition to no major need to reuse the interfaceat the current moment.

 

As always, I like to make development easier, so for usercontrols that implement the widget interface, I added another base class thatuser controls can take advantage of, which sits between the UserControl baseclass and the ASCX code-behind (see Figure 3). This means the application has acentral place for code to perform the same functionality. On initialization,the user control registers itself with the parent page, as long as itimplements ICustomerWidgetContainer. However, the page doesn?t have to; if itdoesn?t, it simply isn?t registered and the page can?t take advantage of theregistration process.

 

public class CustomerWidgetUserControl :

 UserControl,ICustomerWidget

{

 protected override voidOnInit(EventArgs e)

 {

   base.OnInit(e);

 

   ICustomerWidgetContainercustomerContainer =

     this.Page as ICustomerWidgetContainer;

   if (customerContainer!= null)

     customerContainer.RegisterControl(this);

 }

}

Figure 3: Customerwidget user control base class.

 

So what does this all mean? When the page initializes, thepage knows everything about the widgets on the page because they registeredthemselves with the page. For instance, if there were seven widgets, thecollection contains seven object instances (simply because the widgets inheritfrom CustomerWidgetUserControl).

 

You may also wonder why use the ICustomerWidget interface;this really isn?t necessary. The only reason I used this interface was to constrainwhat could be registered with the page. It prevents any control, user control,or other object from being registered.

 

This approach illustrates essentially three features:

  • User controls can have customized base classes.
  • User controls and pages can implement interfacesto increase functionality.
  • Because of the uses of interfaces, the widgetcould be essentially anything (even a stub or mock class for unit testing).

 

Page Events

When an ASP.NET page executes, the page fires a series ofevents (referred to as the page?s lifecycle) in a specified order. Event firingbegins at the page level, then bubbles down to the individual controls. Anyevents that post back from within a control (custom control events) firebetween the load and pre-render events.

 

Some care is required when managing loading or saving datain these event handlers. Because a page consists of many controls, and usercontrols that represent sections of the page, updates for different sections ofthe page occur at different times. Some controls may be updated oninitialization, some on load, and some before rendering. Some care is requiredin case some portions of the application rely on other portions.

 

When developing applications, I personally believe ASP.NETdevelopers can benefit from the extract method of refactoring, rather thanembedding all the code in the OnInit, OnLoad, or OnPreRender page methods. Thishelps reduce the complexity of the page?s code, especially in these eventmethods. However, sometimes that?s not enough. The ASP.NET site needs torespond to state changes, which is often what ASP.NET code is all about. Forinstance, the page needs to perform an action when the user logs in or logsout. What about the SelectedCustomerChanged event we saw previously? Performingactions when the customer changes data is important, as well.

 

Creating a Custom Event Lifecycle

An event is a response to a change or condition of thestate of the application. For instance, the GridView?s SelectedIndexChangedevent fires when the select link for a different row is clicked. The events inthe page lifecycle suffixed with ?Complete? only fire when asynchronous pageprocessing is enabled.

 

Why not create events in the lifecycle that are dependenton the data? For instance, in our custom page example, the selected customer?skey is stored in the cache. Whenever this key value changes to a new customer,an associated event fires. This is, in a sense, creating a custom lifecycle foryour application. A custom page class is perfect to implement this because allyour ASP.NET pages can inherit from this class, and every control/user controlcan reference this custom page class through its Page property.

 

If you?ve developed custom controls, this approach is verysimilar to handling the IPostBackEventHandler interface. In this approach, acontrol posts back with an event argument (which can be created byPage.ClientScript.GetPostBackClientHyperlink). The control receives thepostback and performs its processing at a specific point in the page lifecycle.This point, whenever the event occurs, happens at the same point in thelifecycle.

 

Similar to custom controls, the key benefits to creatingcustom lifecycle events are enormous because they reduce the amount of pageprocessing (data queries and if checks) that occur when the page runs. Parts ofASP.NET applications can be ?read and react? (the page looks for a change; whenit finds one, it updates the user interface).

 

However, custom page events help reduce the amount of codeby firing a specific event to signify that action; instead of reading andreacting, the code can simply begin its work without doing conditional checks.

 

In our portal example, the user logs in through the Logincontrol and logs out with the LoginStatus control; this works seamlessly withforms authentication. By firing events for logging in and out at the pagelevel, the control event handlers that perform those actions can simply calltwo methods to fire these events, and any other code in the page can respond tothese. Take a look at the events/methods and control event handlers in Figure4.

 

public event EventHandler LoggedIn;

public event EventHandler LoggedOut;

protected virtual void OnLoggedIn(EventArgs e)

{

 if (LoggedIn != null)

   LoggedIn(this, e);

}

 

protected virtual void OnLoggedOut(EventArgs e)

{

 if (LoggedOut != null)

   LoggedOut(this, e);

}

//Login status fires logged out event; call our method to fireour event

protected void lgnStatus_LoggedOut(object sender, EventArgs e)

{

 if (LoggedOut != null)

   LoggedOut(this, e);

}

//Login fires logged in event; call our method to fire our event

void lgnLogin_LoggedIn(object sender, EventArgs e)

{

 this.OnLoggedIn(e);

}

Figure 4: Defininglog in/out events and firing them.

 

All pages that inherit from this custom page class cancall the OnLoggedIn and OnLoggedOut methods to notify the page of theseactions. You may be thinking this approach is not quite as useful. After all,why not let the Login control handle this functionality instead? It fires itsown event that the application could respond to, instead of firing a page-levelevent. In the case of logging in, this may be true; however, I do see oneimmediate design benefit: code encapsulation.

 

By defining code for logged in/out status changes in themethods above, any page that requires some action for these events can make useof it without knowing how the user logs in or out of the system. It might notmake sense when the log-in capabilities are in one central page, but it makessense if the Login control resides in the master page for the site. Transform thisidea for other areas of the application as well, such as theSelectedCustomerKey property in the custom page class (see Figure 5).

 

public Guid SelectedCustomerKey

{

 get

 {

   if(_selectedCustomerKey == Guid.Empty)

   {

     object value = this.Services.Caching.Get(this.Services.

     Caching.GetSafeKey("SelectedCustomerKey"));

     if (value != null)

       _selectedCustomerKey= new Guid(value.ToString());

   }

   return_selectedCustomerKey;

 }

 set

 {

   if(_selectedCustomerKey != value)

   {

     _selectedCustomerKey= value;

       this.Services.Caching.Add(this.Services.Caching.

        GetSafeKey("SelectedCustomerKey"),

        _selectedCustomerKey);

     this.OnSelectedCustomerChanged(EventArgs.Empty);

   }

 }

}

Figure 5: Trackingselected customer key.

 

When the record for the current customer changes, theSelectedCustomerChanged event fires. This allows the page to respond to thisevent and refresh any data that displays information about the customer. Figure6 shows one example for a user control that displays customer orders. When thecustomer changes, the page refreshes.

 

private void BindOrders()

{

 OrdersBAL bal = newOrdersBAL();

 SamplesDataSet.OrdersDataTableordersTable =

   bal.GetCustomerOrders(((CustomerPortalPageBase)

  this.Page).SelectedCustomer);

 

 this.gvwOrders.DataSource= ordersTable;

 this.gvwOrders.DataBind();

}

 

protected override void OnSelectedCustomerChanged(EventArgs e)

{

 base.OnSelectedCustomerChanged(e);

 this.BindOrders();

}

Figure 6: Binding ordersin response to customer change.

 

Although state changes are nice, they aren?t alwaysnecessary. For instance, we talked about how to handle changes to the currentcustomer?s record. In some instances, it?s just as easy to rewrite the informationto the screen. For instance, the customer portal sample displays the basicinformation about the selected customer in a user control. This user controlsimply accesses the SelectedCustomer record in our custom page class and writesthe information to its controls. Though it could respond to a selection change,does it always have to? Because the record is cached, it?s not as critical torespond to the selection change, and simply change the data on pre-rendering,as shown in Figure 7.

 

protected override void OnPreRender(EventArgs e)

{

 base.OnPreRender(e);

 

 CustomerPortalPageBasepage = this.Page as CustomerPortalPageBase;

 if (page == null)

   throw newException("This control is not on a page that inherits

                       fromthe correct base class");

 

 if (page.SelectedCustomer!= null)

 {

   this.lblFirstName.Text= page.SelectedCustomer.FirstName;

   this.lblLastName.Text =page.SelectedCustomer.LastName;

   this.lblAccountNumber.Text= page.SelectedCustomer.AccountNumber;

    this.mvwCustomerDetails.ActiveViewIndex = 1;

 }

 else

   this.mvwCustomerDetails.ActiveViewIndex= 0;

}

Figure 7:Displaying customer data in pre-render.

 

Loosely Coupled Approaches

If you?ve read books on object-oriented design or designpatterns, like Code Complete by SteveMcConnell, Design Patterns by Gammaet al., or one of the many others, you may have read about developing businesscomponents that are less coupled with each other. What this means is that themore the system can work with abstraction, the less development effort thereneeds to be to create a solution or change it in the future.

 

I mentioned that some of the custom events added to thecustom page lifecycle could be a LoggedOut event, which responds to a log-outrequest in the system. Suppose that log-out mechanism is through the use of theLoginStatus control. The placement of this control is in the master page. Mygoal in this section is to not couple the LoginStatus control to the masterpage, which the page will make use of. Rather, through some abstraction, I?mgoing to make this as loosely coupled as I can.

 

Looking at a more tightly coupled approach, I easily couldhave embedded an event handler in the master page that responds to logging theuser out of the system, as shown here:

 

protected override void OnInit(EventArgs e)

{

 base.OnInit(e);

 

 LoginStatus logoutControl=

   this.Page.Master.FindControl("LoginStatus1")

  as LoginStatus;

 logoutControl.LoggedOut+= LoggedOutHandler;

}

 

To avoid that coupling, I need the use of an interface. Thisinterface fires an event to let the page know the user logged out. Essentially,this event lets me ?bubble up? a logged-out event that the page attaches to,and fires its own LoggedOut event. I?ve included the definition of the event inFigure 8, as this interface is implemented for the master page.

 

public interface ILoggingOutControl

{

 event EventHandlerLoggedOut;

}

public partial class Site : System.Web.UI.MasterPage,ILoggingOutControl

{

 

 public event EventHandlerLoggedOut;

 

   protected void lgnStatus_LoggedOut(objectsender, EventArgs e)

 {

   //Bubbles up the loggedout event from login status

   //to master page, whichbubbles up to the page

   if (LoggedOut != null)

     LoggedOut(this, e);

 }

}

Figure 8: Master pageimplementation of IloggingOutControl.

 

The CustomerPortalPageBase class has aRegisterLoggingOutControl method (see Figure 9). This method takes a referenceto an object with the new interface and attaches to the event. This finishesthe event bubbling process.

 

public void RegisterLoggingOutControl(ILoggingOutControl control)

{

 if (control == null)

   throw newArgumentNullException("control");

 

 control.LoggedOut +=LoggedOutHandler;

}

Figure 9: Custom pageclass tapping into LoggedOut event.

 

Using an interface avoids passing in a reference to aspecific control; I could have passed in the LoginStatus control referenceinstead, because this control does define that event. However, that poses twoproblems. First, if I change the parameter of that method from the ILoggingOutControltype to LoginStatus type, the application will require a LoginStatus control asthe sole interface control to log the user out of the system ? which is notwhat I?m trying to accomplish.

 

I could have made the parameter a reference to an objectof type Control; however, the Control class itself doesn?t define the LoggedOutevent, which would require me to use reflection to look for a LoggedOut event. WhileI do like reflection and see some great benefits with it, it does add someperformance overhead, and I was trying to stay away from that.

 

Because the master page implements this interface, thelast requirement is to change the initialization method to pass in a referenceto the master page to the RegisterLoggingOutControl method. The new OnInitdefinition is shown in Figure 10.

 

protected override void OnInit(EventArgs e)

{

 base.OnInit(e);

 

 CustomerPortalPageBasepage = this.Page

   as CustomerPortalPageBase;

 if (page != null)

   page.RegisterLoggingOutControl(this);

}

Figure 10:Registering the master page with the custom page class.

 

It may require more code, but this solution is a littlemore flexible and abstract. This implementation even allows multiple controlsto log the user out of the system. To take the abstraction to a different leveland reduce the amount of code in the master page, another solution is to createa wrapper for the LoginStatus control, which implements the ILoggingOutControlinterface (see Figure 11).

 

public class LoginStatusWrapper : ILoggingOutControl

{

 private LoginStatus_loginStatus = null;

 public event EventHandlerLoggedOut;

 

 publicLoginStatusWrapper(LoginStatus loginStatus)

 {

   if (loginStatus ==null)

     throw newArgumentNullException("loginStatus");

 

   _loginStatus = loginStatus;

   _loginStatus.LoggedOut+= LoginStatusWrapper_LoggedOut;

 }

 

 protected virtual voidOnLoggedOut(EventArgs e)

 {

   if (LoggedOut != null)

     LoggedOut(this, e);

 }

 

 voidLoginStatusWrapper_LoggedOut(object sender, EventArgs e)

 {

   this.OnLoggedOut(e);

 }

}

Figure 11: Using awrapper class to implement the log-out interface.

 

The master page can pass in a reference of typeLoginStatusWrapper to the RegisterLoggingOutControl, by passing in a referenceto a new LoginStatusWrapper(this.lgnStatus). A wrapper is a useful patternbecause it can extend an existing class (even if that class is sealed),providing extra functionality not originally present.

 

Specialized Development

With a global page class, you may wonder if it?s worth itto use these concepts in other areas of development. Specialized page classesused for not-so-widely scaled areas of the application can be a benefit. Afterall, the more code that exists in a Web class library, the more testable theapplication is. What I mean by that is that unit tests can actually instantiatethe page and test out the various properties or methods, which is a benefit tothe application.

 

However, I?d advise caution when it comes to specializedpage or user control classes. Putting too much logic in custom page classes cancreate a maintenance nightmare, especially if the code controls the entire pagefrom the code-behind. It?s more verbose to write code in the code-behind thanset values in the ASPX markup.

 

I mention the avoidance of this because customized pageclasses tend to grow in size and become unmanageable if you try to incorporateall the potential logic in the code-behind class. Splitting the logic betweenthe custom page class and the ASPX class can be a challenge to remember whatcode was implemented where. I tried this approach early in my developmentcareer, incorporating as much logic into the page class as possible tofacilitate unit testing ? I even wrote an article on the subject (see ?ASP.NETOOP and Unit Testing? at http://aspalliance.com/1328).

 

Moving logic to a code-behind page class for two ASPXpages that perform mostly the same function or putting a few methods into aspecialized page that?s reused across a subset of ASPX pages can be a goodidea. However, trying to implement all your logic for an ASPX page in acode-behind class that resides in a class library solely for the purpose ofunit testing can be a maintenance nightmare. The point I?m trying to make withthis paragraph is to find a balance between the two options.

 

I?m not against writing specialized custom class pagesthat are only used by one to five pages or so. Although it?s good to have acommon set of features available in one centralized area, on occasion this canbe accomplished through helper classes defined as static, which can be createdin a class library or somewhere else. A static helper class facilitates reuseand testability, while not making the application more overtly complex. Interfacesalso can help offset some of the challenges, as well.

 

Concurrency/Caching Issues

In the previous custom page example, the key of thecustomer resides in cache, accessible through the base class. This allows thepages to retrieve the information about the customer. The customer recorditself could have been stored in cache instead; this would have provided directaccess to the data, with a potential cost.

 

The questions you must think about when designingdata-driven applications are:

  • How frequently do data changes occur?
  • Is data generally static or dynamic?
  • Do data changes occur in real-time fashion?

 

There aren?t any easy answers to these questions. If thedata changes a lot, it might be best to store the key in the page class. Thedownside to this is the customer record is re-queried to provide the samecustomer information during multiple page loads, potentially. In addition,caching the record may be beneficial if only one user changes the details aboutthat customer; however, if many users change the customer record, it?s betterto re-query the data every time, with adequate locking to ensure integrity ofthe data.

 

Certain architectures may be more of a challenge withthese issues; for instance, LINQ to SQL requires a current data context, anddata cached outside that current context cannot be used in conjunction with thedata context. If you do go against the data context with data that was queriedin a ?past lifetime? of the data context, an exception is thrown. However, youcan access the object?s properties and relationships like any other businessobject.

 

In addition, what happens when the user logs out of thesystem, or closes the browser? Although there are only so many details you canhandle inside the Web browser, the question about this issue is, should thedata remain in cache when the user isn?t accessing the site, and for how long?

 

Conclusion

This article covered a variety of topics focused ondeveloping logic in business applications. It looked at using a custom pageclass to facilitate the flow of the application, and acts as sort of a Singletonclass. It also touched on the use of interface to open up communication betweenthe page and user control. I like the interface option when developing inASP.NET because it allows for testability, along with custom page classes. Puttingmore logic in the page class (that exists in a class library) can aidtestability; however, more logic in the page class starts to make that classmore specialized for certain areas of the application, which can hurt maintainability.

 

C# and VB.NET sourcecode accompanying this article is available for download.

 

Brian Mains is aconsultant with Computer Aid Inc., where he works with non-profit and state governmentorganizations. He was awarded the Microsoft Most Valuable Professional award inJuly 2007 and 2008 and has been active on several .NET forums and Web sites.You can catch him on his blog at http://dotnetslackers.com/Community/blogs/bmains/default.aspx.

 

 

 

Add a Comment

There are no comments to display. Be the first one!
You must log on before posting a comment.

Are you a new visitor? Register Here

advertisement




Comments from the DevConnections Community

Join our community of development pros.

Windows problem

I all, I have a problem on my Windows Vista that began afetr the purchase of an external Hard Disk Freecom. A few days afetr the purchase I discon...

Most Recent Posts

GOOGLE LINKS
SPONSORED LINKS
FEATURED LINKS