A new way to DataBind()

The Problem 

I've been thinking alot recently about the problems with data binding (and they're alot). There are some patterns that play well with ASP.NET (and are repeated everywhere) and some that don't quite fit the model. One of those patterns that don't mesh well is, setting the DataSource property of any of the DataControls. Before ASP.NET 2.0 and DataSource controls we'd have to set the DataSource property and manually call DataBind.

What's wrong with this you may ask? When are you supposed to DataBind? Page_Load ? Page_Init? Page_PreRender? I'm sure anyone that has had to manually data bind has had the problem of figuring out which code goes in and out of the IsPostBack block.

Whats the solution?

I'm looking at an alternative approach to this problem. Event handlers are nice because we all feel confident that the author of the event knows when that event should be raised. No need to figure out the postback logic because the control author already thought about that. All you have to worry about is handling the event.

I propose a new hypothetical event OnDataRetrieveing, that would be on all DataControls in conjunction with a new flag AutoBind to enable it.

Let's take a look at a sample implementation of a derived GridView control that has this new behavior. First the markup:

<custom:AutoGridView 
    OnDataRetrieving="OnDataRetrieving"
    OnPageIndexChanging="OnGridViewPageIndexChanging"
    OnRowEditing="OnGridViewRowEditing"
    OnRowCancelingEdit="OnGridViewRowCancelingEdit"
    AllowPaging="true" 
    runat="server" 
    ID="GridView1" 
    AutoGenerateEditButton="true"
    AutoBind="true">
</custom:AutoGridView>

Looks pretty similar to the regular GridView besides the AutoBind="true" flag and OnDataRetrieving event.

And the code behind

protected void OnGridViewRowCancelingEdit(object sender, GridViewCancelEditEventArgs e) {
    GridView1.EditIndex = -1;
}
 
protected void OnGridViewPageIndexChanging(object sender, GridViewPageEventArgs e) {
    GridView1.PageIndex = e.NewPageIndex;
}
 
protected void OnGridViewRowEditing(object sender, GridViewEditEventArgs e) {
    GridView1.EditIndex = e.NewEditIndex;
}
 
protected void OnDataRetrieving(object sender, DataBindingEventArgs e) {
    NorthwindDataContext db = new NorthwindDataContext();
    e.DataSource = db.Products;
}

We write code as we normally would if we'd been binding manually, but instead, we assign our data to the DataSource property in the DataBindingEventArgs (Which gets called at the "right" time).

So what is the right time to Databind and why does this control do it better than you? I'm not sure :) but let's look at the source of this control.

public override object DataSource {
    get {
        return base.DataSource;
    }
    set {
        if (!_settingDataSource && AutoBind) {
            throw new InvalidOperationException("When AutoBind is enabled, setting the DataSource property explicitly is not allowed.");
        }
        base.DataSource = value;
    }
}
 
protected override void PerformSelect() {
    if (AutoBind) {                
        DataBindingEventArgs args = new DataBindingEventArgs();
        OnDataRetrieving(args); 
        _settingDataSource = true;
        DataSource = args.DataSource;
        _settingDataSource = false;
    }
    base.PerformSelect();
}
 
protected override void EnsureDataBound() {
    if (RequiresDataBinding && (AutoBind || IsBoundUsingDataSourceID)) {
        DataBind();
    }
}

The most interesting method is EnsureDataBound. This method is called from PreRender and the original condition for making the control DataBind is:

if (this.RequiresDataBinding && ((this.DataSourceID.Length > 0) || this._requiresBindToNull)) {
    this.DataBind();
    this._requiresBindToNull = false;
}

In the case of manually binding this would never be called unless _requiresBindToNull is true. Our AutoGridView control changes this logic by detecting the AutoBind flag and continues to data bind as usual in PreRender.

If you interested in when the event gets called you can put some break points in the control's code. What do you think about this alternative?

Here is a link to the source.

9 Comments

  • How about making an ItemsSource property and having it work like it does in Silverlight/WPF?

  • @ Joe Chung The statelessness of the web make things very different from Silverlight/WPF.Alot of the complication comes from the ASP.NET page life cycle and people not understanding how and when things get run. Though it'd be interesting to compare the 2 technologies and see what the major differences are.

  • @Bart Verkoeijen We are not using any data source controls here. Usually if you the DataSource property you have to *manually* call DataBind so in a sense this is Automatic.

  • @davidfowl Basically your solution is the same as currently implemented by the Data Source Model in ASP.NET 2.0. Data Source controls encapsulate the same functionality as your events for the same purpose. The data source is accessed when needed and then performs the fetching logic, just as in your event handlers.
    The difference is where this data binding logic is located. Now you are implementing this in the page and attaching it to the visual control, the one consuming the data. With data sources the data fetching logic is nicely separated from the visual control.
    An alternative to your solution implemented with the Data Source Model would be a custom data source with a similar data fetch event. The benefit of this data source control is that the fetching logic is not in the visual control.
    So therefore I think the AutoBind name is misleading as there is already automatic binding available using the Data Source Model from ASP.NET 2.0.
    But, if you are developing data bound controls for ASP.NET 1.x, then I guess this would be an easy alternative.

  • @Bart Verkoeijen Yes it is the same model, but I don't agree that its for developing for ASP.NET 1.x. Despite the flexibility of DataSource controls, a surprising amount of people still opt into databinding manually even with the ASP.NET 2.0 goodness. Once again AutoBind should only be used in conjunction with setting the DataSource manually, if your already using Data source controls and your happy then more power to you :) (DataSource controls are great!).

  • Its hard to take an approach seriously when you see databinding "code" - in html mark up.
    I get it for a quick and dirty web page but you head down a slippery slope when you do that.

    I'd rather see everything in the codebehind or in a server control so I at least get some compiler support and my pages don't bust when someone wants to refactor the names of things on the middle tier.

  • @Dave T Are you talking about hooking up the events?

  • I agree with Bart. Once I discovered all of the advantages of using a DataSourceControl (e.g. automatic paging support), I rarely use DataSource and DataBind().

    In fact, I use DataSourceID on all of my GridViews and still use DataSource/DataBind() for simple drop downs.

    But honestly, the DataSource/DataBind() model has been around for so long, I haven't had issue with dealing with IsPostBack.

    I do like the concept, but I think the better approach would be to use a DataSourceControl

  • Here is something else to think about. &nbsp;There are still situations where I have to manually bind my data, even when using a DataSourceControl.
    Suppose you have a GridView and Add/Update/Delete actions (that aren't directly integrated into the grid). &nbsp;For example, I have an "Add..." button that pops up a "dialog" and allows the user to enter the information for the new entry. &nbsp;After saving the new item, I have to manually rebind my GridView.
    But you have to be careful here too. &nbsp;If you select multiple entries and "delete", then you don't want to rebind until after all of the items have been deleted. &nbsp;(oh, and of course, you want to reset your selections and paging).
    What would be really interesting to me is if the data bound control can detect "dirty" data. &nbsp;This would be similar to a cache with cache dependencies. &nbsp;The trick is making something like this work in the context of statelessness.
    I've some of my own thoughts on how to do this, but I would be interested in what other people are thinking.

Comments have been disabled for this content.