|
Who Are We?Fluent Consulting is a software development and consulting firm that specializes in enterprise application integration, web applications, and software product development. We are a dedicated team focused on providing the highest level of quality and value for our clients. Please feel free to visit our corporate site or get in touch! |
Aspect Oriented Components: An Introduction
I'd like to describe a component development strategy that we've been using at Fluent Consulting for some time that has proven very useful. It has become common knowledge that software functionality which is implemented in a similar manner over and over again is a good candidate for being re-written and deployed as a component. In the .NET framework this would typically be done by wrapping your reusable functionality into a custom server control and deploying it as a standalone DLL, which can be dropped into the /bin folder of any application.
In many situations these types of components are very valuable, and we have built up a library of such components that we often use when developing applications. There are times, however, when the code we would like to be able to reuse is not standalone functionality, but rather needs to be interwoven with existing code. In these cases we have adopted a modified component development strategy, based on aspect-oriented software development. For more information about aspects you can review a list of papers published by XEROX PARC (where AOP was invented), a list of aspect development tools, or an article about implementing aspects in C#. I'll describe our methodology with an example.
The .NET framework offers a number of rich web controls which can be bound to data sources and populated at runtime. The DataGrid is the best known of these. Suppose you wanted to make a few changes to how the DataGrid control worked; here's an example of an enhancement. If you bind a DataGrid to a data source which is empty at runtime, when the control renders you will see the header and footer but no contents of the DataGrid. A better behavior would be for the DataGrid to not display the header and footer, but rather display to the user a friendly message saying "No results were returned from your search." or something to similar effect.
The traditional strategy for implementing this feature as a component in .NET would be to create a custom server control, which inherits from the DataGrid control, and adds the required functionality to handle empty data sources. There are a number of considerations which make this approach less than ideal however:
- Because the custom control inherits from the DataGrid control, if you'd like similar behavior for similar data-bound controls such as RadioButtonLists or Repeaters, you would have to create new custom controls for each one, subclassing its respective parent class.
- If you want to incorporate this new functionality into existing code, you would have to change all of your existing DataGrids to implement your new control class.
- Using a custom server control tag in your .aspx page instead of the built-in ASP.NET DataGrid control tag, you will lose the nice Visual Studio .NET tag auto-completion features. These IDE completion features can be very useful on controls as complex as the DataGrid, which have many public properties and subtemplates.
A better solution would be to implement this behavior as an Aspect-Oriented Component. With this methodology, our component does not reimplement any DataGrid features directly, but rather hooks in to an existing DataGrid control at runtime and modifies its properties as needed. The original DataGrid control tag remains as is. Here's a code-snippet of the source for our control:
using System; using System.Web.UI.WebControls; using System.Web; using System.Web.UI; namespace Fluent.EmptyRepeaterPanel { /// <summary> /// Fluent.EmptyRepeaterPanel is an aspect oriented component which checks a data source /// bound to a repeater at runtime. If the data source is empty, the repeater is hidden /// and the contents of the panel are shown in its place. /// </summary> public class EmptyRepeaterPanel : Panel, INamingContainer { private string control; public EmptyRepeaterPanel() { } protected override void AddParsedSubObject(object obj) { this.Controls.Add((Control)obj); } protected override void OnPreRender(System.EventArgs e) { base.OnPreRender(e); string controlName = control.Trim(); try { Control c = (Control) Page.FindControl(controlName); if (c is DataGrid) { DataGrid dg = (DataGrid) Page.FindControl(controlName); if (dg.Items.Count == 0) { dg.Visible = false; } else { this.Controls.Clear(); } } else if (c is ListControl) { ListControl lc = (ListControl) Page.FindControl(controlName); if (lc.Items.Count == 0) { lc.Visible = false; } else { this.Controls.Clear(); } } else if (c is Repeater) { Repeater r = (Repeater) Page.FindControl(controlName); if (r.Items.Count == 0) { r.Visible = false; } else { this.Controls.Clear(); } } else { throw new ApplicationException(@" Control not a valid type. Must be a ListControl, DataGrid, or Repeater, or a type inherited from one of these."); } } catch (System.NullReferenceException nre) { throw new ApplicationException(@" EmptyRepeaterPanel unable to find a control named " + controlName + ".", nre); } } /// <summary> /// ID of the Repeater WebControl to hide if it's data source is empty /// </summary> public string Control { get { return control; } set { control = value; } } } }
This code can be compiled down into a DLL and referenced from any application we need to use it in. To make use of it, first we need to register it in our page like this:
<%@ Register TagPrefix="fluent" Namespace="Fluent.Format" Assembly="Format" %>
In this case our DataGrid is implemented as it normally would, but the .aspx page has an additional tag which modifies the behavior of the DataGrid.
<fluent:EmptyRepeaterPanel Runat="server" Control="TestGrid"> <asp:Label Runat="server" Font-Bold="True" ForeColor="red">Sorry, search returned 0 results.</asp:Label> </fluent:EmptyRepeaterPanel> <asp:DataGrid Runat="server" ID="TestGrid" AutoGenerateColumns="False" ShowFooter="True"> <Columns> <asp:BoundColumn DataField="SSN" HeaderText="SSN" /> <asp:BoundColumn DataField="FNAME" HeaderText="FNAME" /> <asp:BoundColumn DataField="LNAME" HeaderText="LNAME" /> </Columns> </asp:DataGrid>
With this approach we've made it quite easy to modify the behavior of existing native .NET web controls. Aspect Oriented Components are a clean and simple way to add powerful new functionality to your code. In the coming weeks, we will be describing our approach in further detail and discussing promising potential uses.
Your ariticle is very usefull, but it has one error in the web control file code. Name of the property control should be RepeaterToHideIfEmpty instead of control.
Posted by: Sanjay at February 15, 2004 06:55 PMThanks, Sanjay.
There was indeed a typo in my example. I've renamed the RepeaterToHideIfEmpty property to Control.
Posted by: Jeff Heuer at February 17, 2004 11:20 AM


