Mapping business objects to ASPX pages

While going through the ASP.Net forums, I stumbled across a post where a guy asked:

I’m having trouble coming up with a good solution for programmatically mapping members of a business object to control elements on our UI. I’m wondering if someone else has done something similar and might have some useful suggestions.

My reply was a rather lengthy one in which I talked about an approach I recently used in the rewrite of my online baseball game, CSFBL. Since I eventually wanted to blog about this stuff, I figured I’ll just copy/paste the post I made on the forums below…


I struggled with this a bit (struggled in the sense that I couldn’t come to agreement with myself on how to do it!)… the following is what I settled on.

All my business objects are created using CodeSmith templates for Paul Wilson’s O/R mapper. I wanted the web interface to be able to use these directly from both the code-behind and the ASPX page. (Yes, I know the deal about allowing your presentation to be able to edit your data; I’m the only one working on the project, and I can easily refactor out that functionality to factory classes later.

The main reason the approach below worked on my project was because any given web page displayed the properties accessed through a single business object. For example, if this was a contact management web app (it isn’t), an instance of the Contact business class would be effectively “bound” to the ContactDetails.aspx web page. Note that the approach below can also be tailored to allow similar behavior with user controls (i.e. a user control is ‘bound’ to a specific business object).

To do all this required the creation of a few new classes in the web project.

The PageBase class overrides the default Page class, and adds one key function: a call to the page’s DataBind() method in the PreRender stage. This does two things: (1) it eliminates the need to call a DataBind() class in your page’s objects (at least I haven’t found anything that required otherwise), and (2) it enables us to bind in the ASPX (more on that soon).

public class PageBase : System.Web.UI.Page
{
 protected override void OnPreRender( EventArgs e )
 {
 this.DataBind();
 base.OnPreRender(e);
 }
}

The IDataView generic interface exposes a LoadData method and a simple “Data” property.

interface IDataView<t>
{
 T Data { get; }
 void LoadData();
}

The DataViewPage inherits our PageBase and implements the IDataView interface.

public abstract class DataViewPage : PageBase, IDataView
{
 private T _data;
 public T Data
 {
 get { return _data; }
 set { _data = value; }
 }

 protected override void OnInit( EventArgs e )
 {
 this.LoadData();
 if ( this._data == null )
 throw new DataLoadException("Unable to load data for the current page.");

 base.OnInit(e);
 }

 public abstract void LoadData();
}

Finally, a real web page. The web app itself is for a baseball game (the next iteration of CSFBL), so the sample below is the binding of a baseball league to the “LeagueAdmin” web page.

public partial class LeagueAdmin : DataViewPage
{
 public League League
 {
 get { return this.Data; }
 }

 public override void LoadData()
 {
 this.Data = League.RetrieveByKey(1);
 }
}

There’s a few key points to note on how I created the codebehind for the web page:

  • I exposed the Data property (which, thanks to generics, would still be the correct object type, in this case League) using a name other than Data (i.e. I used the property name League). This is merely for convenience and code readability.
  • The LoadData() method (from the interface) populates the Data property by loading from the business model (RetrieveByKey is a method on the League object that returns an instance of the League with the given key).

By doing this, I can do the following in my ASPX page:

Note that I used inline databinding < %# %> and not just < %= %>. The main reason for this is that the inline databinding does not get called until the OnPreRender event (remember our PageBase class?), which helps in situations where you have a postback that changes the business object data — by using databinding I was able to guarantee that this happened after any event-related data changes.

Now I don’t know if this is a great thing or a bad thing, but it definitely solved some problems for me. Oh, and note that all pages on the web site also work with viewstate turned off (another one of my requirements).

I hope I explained all that clearly!

Reordering forums in CommunityServer 1.x

Yes, I know that CommunityServer 2.0 has been released months ago, but considering the effort in upgrading (and my lack of time), I’m still using CommunityServer 1.x on CSFBL. It does a fine job for what I need at this time.

However, one thing perturbs me — the fact that new forums in a forum group aren’t added in the correct place when you want alphabetical sorting. There’s a good reason for this: CommuntiyServer manages sort order by using a SortOrder value in the database, allowing you to choose any sort order. However, this works against you when you want to use plain old alphabetical sorting.

The solution is to run a SQL script to update the SortOrder value of each forum such that they will be displayed alphabetically. The following SQL script will show you the forums, current sort order, and new sort order for all forums in a given group.

select sectionid, name, sortorder,
( select count(*) from cs_sections
where groupid = cs.groupid and name < cs.name ) as new_sortorder from cs_sections cs where groupid = 8 order by sortorder asc [/source] Pretty simple. You can get the GroupID by looking at the same table, or by looking at the ForumGroupID in the URLs on your web site. To apply these changes: [source='sql'] update cs_sections set sortorder = ( select count(*) from cs_sections where groupid = cs.groupid and name < cs.name ) from cs_sections cs where groupid = 8 [/source] Simple and relatively harmless -- and a heck of a lot easier than trying to use the (buggy) reordering of forums in the admin console!

The strong arm of Google AdSense

I’ve been using Google AdSense on my sites for quite some time. One of the largest of those sites is CSFBL, my online baseball game.

Google shut down that site’s access to AdSense because of the following statement on the “Support Us” page:

You’ve all seen the ads throughout the site. Please support CSFBL by supporting these advertisers!

They stated that such a statement “contained language that encouraged clicking on ads”. I could see if I said, “Click on the ads on this site to make us money” — but that’s not what I said. After all, I’ve heard radio ads (promoting radio advertising) state, “Support this station by supporting its advertisers.”

Perhaps I should say, “Please visit the companies that advertise on this site, but please don’t click on their ads for fear that such clicks will be deemed invalid.”

I could argue, but why bother — I won’t win, and it’s a heck of a lot easier to complain here, remove the one line of text from the Web page, and get on with things. Which is exactly what I’m doing. 🙂