Nested declarative data binding in ASP.NET 2.0

Onion Blog

Syndication

I'm becoming more and more enamored with the declarative databinding model of ASP.NET 2.0 the more that I use it. One issue that it deals with rather nicely is the asymmetric nature of join queries and their corresponding update statements, which I find to be one of the most common queries used in Web applications since you are often presenting data from a table that contains foreign-key references to other tables containing the complete name and description (or whatever the extra data is).
 
To show what I mean, take the employees table in the pubs database (sample database that comes with SQL server). The employees table has two foreign key columns in it, job_id and pub_id, referencing the publishers and jobs tables respectively. To properly present the data, you might build a query with two inner joins to retrieve the names of the jobs and publishers:
 
SELECT e.emp_id, e.fname, e.minit, e.lname, e.job_id, e.job_lvl, e.pub_id, e.hire_date, job_desc, pub_name
FROM employee AS e
INNER JOIN jobs AS j ON e.job_id=j.job_id
INNER JOIN publishers AS p ON e.pub_id=p.pub_id
 
However, if you are performing an update or an insert into the table, you need to specify the foreign key id fields directly:
 
UPDATE [employee] SET [fname] = @fname, [minit] = @minit, [lname] = @lname, [job_id] = @job_id, [job_lvl] = @job_lvl, [pub_id] = @pub_id, [hire_date] = @hire_date WHERE [emp_id] = @emp_id
 
Here is a sample SqlDataSource that encapsulates these two commands (using just Select and Update to keep things simple):
 
<asp:SqlDataSource ID="_employeeDataSource" runat="server"
        ConnectionString="<%$ ConnectionStrings:pubsConnectionString1 %>"
        SelectCommand="SELECT e.emp_id, e.fname, e.minit, e.lname, e.job_id, e.job_lvl, e.pub_id, e.hire_date, job_desc, pub_name FROM employee AS e INNER JOIN jobs AS j ON e.job_id=j.job_id INNER JOIN publishers AS p ON e.pub_id=p.pub_id"
        UpdateCommand="UPDATE [employee] SET [fname] = @fname, [minit] = @minit, [lname] = @lname, [job_id] = @job_id, [job_lvl] = @job_lvl, [pub_id] = @pub_id, [hire_date] = @hire_date WHERE [emp_id] = @emp_id">
  <UpdateParameters>
    <asp:Parameter Name="fname" Type="String" />
    <asp:Parameter Name="minit" Type="String" />
    <asp:Parameter Name="lname" Type="String" />
    <asp:Parameter Name="job_id" Type="Int16" />
    <asp:Parameter Name="job_lvl" Type="Byte" />
    <asp:Parameter Name="pub_id" Type="String" />
    <asp:Parameter Name="hire_date" Type="DateTime" />
    <asp:Parameter Name="emp_id" Type="String" />
  </UpdateParameters>
</asp:SqlDataSource>
 
Now, if you attach a GridView to this data source you typically will want to give the user a drop-down list in update mode so that she can select from the proper list of publishers and jobs. This is where declarative data sources shine, because you can create a template column for the job and publisher columns, and in their UpdateItemTemplates, specify a DropDownList with an associated DataSourceID attribute pointing to another declarative data source prepared to retrieve all of the jobs and publishers separately in a nested databind. Futhermore, you can use a databinding expression to set the selected element of the dropdown to the currently selected value for that column in the current row. Even better, since these lookup tables are unlikely to change very often, you can enable caching on their data sources and keep them in memory, all through properties of the data source control. Plus, because of Control state, all of this works even with ViewState disabled (as I've done here). So here is an example of a GridView pointing to the DataSource listed above, with two nested data binds when rendered in Update mode, grabbing table from a pair of lookup tables cached in memory (after the first access) for 200 seconds.
 
<asp:GridView ID="GridView1" runat="server" AllowPaging="True" AutoGenerateColumns="False"
                    EnableViewState="false" DataKeyNames="emp_id" DataSourceID="_employeeDataSource"
                    EmptyDataText="There are no data records to display.">
  <Columns>
    <asp:CommandField ShowEditButton="True" />
    <asp:BoundField DataField="emp_id" HeaderText="emp_id" ReadOnly="True"
                          SortExpression="emp_id" Visible="False" />
    <asp:BoundField DataField="fname" HeaderText="fname" SortExpression="fname" />
    <asp:BoundField DataField="minit" HeaderText="minit" SortExpression="minit" />
    <asp:BoundField DataField="lname" HeaderText="lname" SortExpression="lname" />
    <asp:TemplateField HeaderText="Job" SortExpression="job_id">
    <EditItemTemplate>
      <asp:DropDownList runat="server" ID="_jobDropDown" DataSourceID="_jobDataSource"
                                EnableViewState="false"
                                AppendDataBoundItems="false" DataTextField="job_desc"
                                DataValueField="job_id" SelectedValue='<%# Bind("job_id") %>'>
        <asp:ListItem Selected="true" Text="[Select a job]" Value="-1" />
      </asp:DropDownList>
    </EditItemTemplate>
    <ItemTemplate>
      <asp:Label ID="Label1" runat="server" Text='<%# Bind("job_desc") %>' />
    </ItemTemplate>
  </asp:TemplateField>
  <asp:BoundField DataField="job_lvl" HeaderText="job_lvl" SortExpression="job_lvl" />
  <asp:TemplateField HeaderText="Publisher" SortExpression="pub_id">
    <EditItemTemplate>
      <asp:DropDownList runat="server" ID="_publishersDropDown" DataSourceID="_publishersDataSource"
                                EnableViewState="false" AppendDataBoundItems="false" DataTextField="pub_name"
                                DataValueField="pub_id" SelectedValue='<%# Bind("pub_id") %>'>
        <asp:ListItem Selected="true" Text="[Select a publisher]" Value="-1" />
      </asp:DropDownList>
    </EditItemTemplate>
    <ItemTemplate>
      <asp:Label ID="Label2" runat="server" Text='<%# Bind("pub_name") %>'></asp:Label>
    </ItemTemplate>
  </asp:TemplateField>
  <asp:BoundField DataField="hire_date" DataFormatString="{0:d}" HeaderText="hire_date"        
                        HtmlEncode="False" SortExpression="hire_date" />
  </Columns>
</asp:GridView>
 
<asp:SqlDataSource ID="_jobDataSource" runat="server"
                            SelectCommand="SELECT job_id, job_desc FROM jobs"
                            EnableCaching="true" CacheDuration="200"
                            ConnectionString="<%$ ConnectionStrings:pubsConnectionString1 %>" />
<asp:SqlDataSource ID="_publishersDataSource" runat="server"
                            SelectCommand="SELECT pub_id, pub_name FROM publishers"
                            EnableCaching="true" CacheDuration="200"
                            ConnectionString="<%$ ConnectionStrings:pubsConnectionString1 %>" />

Posted Oct 31 2005, 09:30 AM by fritz-onion
Filed under:

Comments

Jason Haley wrote Interesting Finds
on 11-01-2005 4:09 PM
Wouter van Vugt wrote Nested declarative databinding in ASP.NET 2.0
on 11-02-2005 10:12 PM
Christopher Steen wrote Link Listing - November 3, 2005
on 11-03-2005 10:06 AM
ADO.NET Provider Model Fundamentals [Via: dhayden ]
Ajax's responseXML Error [Via: ]

Anticipated...
neil wrote re: Nested declarative data binding in ASP.NET 2.0
on 12-02-2005 2:08 AM
great article, just exactly what i was looking for fritz. and btw, your 15 part webcasts on 2.0 are fantastic as well.
Chris Pels wrote re: Nested declarative data binding in ASP.NET 2.0
on 12-09-2005 12:23 PM
Have you found a way to bind to a property in a complex object from an ObjectDataSource, e.g., the PostalAddress.City in the datasource? The Bind() method and BoundField only seem to work w/ simple properties?
newbie wrote re: Nested declarative data binding in ASP.NET 2.0
on 12-21-2005 2:36 PM
when implementing the technique i get the following when i do an update:

Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.

your thoughts is greatly appreciated.
midwest wrote re: Nested declarative data binding in ASP.NET 2.0
on 12-22-2005 6:26 AM
Fritz, this was helpful. thanks!

Now, if I set the DataObjectTypeName attribute (for ObjectDataSource) to MyBusinessLayer.Employee (which has properties like fname, minit, lname, hire_date, emp_id, job, and pub ... with job and pub returning objects of the type Job and Publisher), how would I bind the controls in the Gridview template columns?

If I change the Employee class to have properties like jobname and publishername, it works ... but is there a better way?

Thanks!
ben wrote re: Nested declarative data binding in ASP.NET 2.0
on 01-20-2006 9:29 PM
thank you SO much! a great quick-start among all the mud out there.

keep it coming.
Adam Toth wrote re: Nested declarative data binding in ASP.NET 2.0
on 01-31-2006 1:39 PM
This is an old thread, but hopefully you are watching this.

I'm curious what happens if the top-level container has an identical column/property name as the nested bound container?

For example, say I have a formview that displays one record (say a trouble-ticket). This record has an insert_time column.

In this form view, I do a neseted databind to populate a repeater with comments (each trouble ticket has a collection of comments, stored in a comments table, with a foreign key field relating back to the trouble-ticket). What happens if the comment data source has an insert_time column as well?

What will happen when I do:

<asp:Label Text='<%# Eval("insert_time") ... />

in the nested ItemTemplate of the repeater?

Adam
Kristin wrote re: Nested declarative data binding in ASP.NET 2.0
on 03-07-2006 9:12 AM
I've set up a similar page with a dropdownlist bound to a datasource. Just as in your demonstration, my DropDownList list is bound to one table for its items and the SelectedValue property is bound to a (lookup) id column for the table associated with the form. Yet when the page loads, I get a format exeption ("Input String was not in a correct format").

To my surprise, the SelectedValue property on asp DropDownList is a string, not an object. I'm now wondering how this ever works.

Any suggestions?
Simone Busoli wrote re: Nested declarative data binding in ASP.NET 2.0
on 03-25-2006 5:00 PM
Hi Fritz, interesting post.

I just wanted to let you know something I found out. In the case you described you can avoid to use JOINS in your queries, because you use external data sources to fill the dropdownlists, but what if you want to display external tables data in ItemTemplate, that's to say using Labels?

You can't do this way because you cannot use external data sources, and you need to use JOINS in the queries.

The problem is that using JOINS VS2005 seems not to be able to auto generate the insert, update and delete statements and methods, which is often very useful. Reading a post from Scott Guthrie I discovered that if, instead of writing the query like

SELECT e.emp_id, e.fname, e.minit, e.lname, e.job_id, e.job_lvl, e.pub_id, e.hire_date, job_desc, pub_name
FROM employee AS e
INNER JOIN jobs AS j ON e.job_id=j.job_id
INNER JOIN publishers AS p ON e.pub_id=p.pub_id

you write

SELECT e.emp_id, e.fname, e.minit, e.lname, e.job_id, e.job_lvl, e.pub_id, e.hire_date,
(SELECT job_desc FROM jobs WHERE job_id = e.job_id) as job_desc, (SELECT pub_name FROM publishers WHERE pub_id = e.pub_id) as pub_name
FROM employee AS e

you obtain the same result and this way VS2005 can autogenerate those statements and methods.

Something useful which doesn't seem to be documented anywhere.
Alphonso wrote re: Nested declarative data binding in ASP.NET 2.0
on 03-27-2006 4:18 AM
I'm getting the same error as "Newbe".

when implementing the technique i get the following when i do an update:

Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.

Anyone solved that problem?
Alphonso wrote re: Nested declarative data binding in ASP.NET 2.0
on 03-27-2006 11:15 PM
I solved the problem discribed above.
Actually I do not know how I did it, unfortunately I erased the old code.
Anyhow, I just read one more blogg and combined the information I found there with what is said on this blogg.
I hope it's fine to put the link:
http://www.mikepope.com/blog/AddComment.aspx?blogid=1423

I think that my mistakes are related to the use of Eval vrs. Bind.
Hope that it helps someone.
Matt wrote re: Nested declarative data binding in ASP.NET 2.0
on 03-28-2006 2:46 PM
My comment is related to Simone's above (http://pluralsight.com/blogs/fritz/archive/2005/10/31/16059.aspx#20785).

I'm finding the need to perform a join in the select in order to get the description of a related piece of data irritating. Since I can setup additional datasources for the dropdownlists why can't I use those to populate the ItemTemplate Label? Maybe I can and I just haven't figured it out yet....anyone know?
Garry Lindsay wrote re: Nested declarative data binding in ASP.NET 2.0
on 03-28-2006 5:53 PM
I like from Simone Busoli :-

SELECT e.emp_id, e.fname, e.minit, e.lname, e.job_id, e.job_lvl, e.pub_id, e.hire_date,
(SELECT job_desc FROM jobs WHERE job_id = e.job_id) as job_desc, (SELECT pub_name FROM publishers WHERE pub_id = e.pub_id) as pub_name
FROM employee AS e

But what happens if the column selects are not tables but xml files!!!

Do I have to use DataTables and Data Relations to get a decent binding? Which means thowing away the sql datasource idea completely?
Simone Busoli wrote re: Nested declarative data binding in ASP.NET 2.0
on 04-02-2006 10:31 PM
@ Matt

Simply because the Label control can't be bound to a data source, be it a DataSourceControl or whatever. The only bindings you can do are to the GridView/DetailsView/FormView parent control's data source fields.

Other than the JOIN solution I explained you can even leave the original select statement intact and do your customized data binding, doing something like:

<asp:label ... Text="<%# GetLabelText() %>" />

Where the GetLabelText() method is defined in your page code. There you will retrieve the data to be bound from the database.

For details look at this post by Fritz: http://pluralsight.com/blogs/fritz/archive/2005/12/16/17507.aspx
Nathan Lee wrote re: Nested declarative data binding in ASP.NET 2.0
on 05-11-2006 6:37 AM
Kristin:

I was also just recently having the same problem with DropDownLists where I wanted to bind to SelectedValue, but one of the values might not match.

If you want to use Bind() on the SelectedValue on a DropDownList, you can inherit from DropDownList and override PerformDataBinding().

I have sample code working on my site:
http://www.zornonline.com/blog/index.php/2006/05/10/aspnet-20-selectedvalue-bind-on-dropdownlists/
Richard wrote re: Nested declarative data binding in ASP.NET 2.0
on 06-14-2006 6:46 AM
When binding to a DropDownList doesn't make sense for the item template, I used a label and handled the DataBinding event. I grabbed the value myself and populated the label from a helper object.

Since I needed the foreign key, I still used a BIND("TYPE_FK") in the item template. My event handler looked something like:

protected void TypeDataBinding( object sender, EventArgs e )
{
Label type = (Label)sender;
if ( type != null )
{
// Grab the type name based on the FK
// already bound into the label's text
type.Text = Types.Get[ type.Text ];
}
}
dropdrownlist get the object wrote re: Nested declarative data binding in ASP.NET 2.0
on 07-08-2006 2:51 PM
If i set the datasource of a dropdrownlist with a ilist of objects, can i get the objects back from the dropdrown list i mean the object selected with propertys an everything?
Nitin wrote re: Nested declarative data binding in ASP.NET 2.0
on 07-11-2006 11:43 PM
Its Nice
Michal Talaga wrote re: Nested declarative data binding in ASP.NET 2.0
on 08-17-2006 2:02 PM
Be sure to check my article on binding to nested properties using more object oriented approach (ObjectDataSource) on my blog under:
http://vaultofthoughts.net/UsingBindWithNestedProperties.aspx
Mike wrote re: Nested declarative data binding in ASP.NET 2.0
on 10-04-2006 8:33 AM
For Simone Busoli,
re:
you obtain the same result and this way VS2005 can autogenerate those statements and methods.

after dropping my data source on my form, I can't seem to get a statment such as yours into the configure select statement wizard screen?
help... thanks
Mike
bhat Np wrote Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control.
on 12-07-2006 2:39 AM
For handling Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control. error , I have used user control based technique which works fine with least coding ..You can check at

http://aspdotnetprojects.googlepages.com/dotnethome

and give me feedback at bhatnp@gmail.com
DOIT659 wrote re: Nested declarative data binding in ASP.NET 2.0
on 07-05-2007 1:59 PM
Thanks, Dude. I searched for a while before such an elegant and complete example was found.

You rock!
egon wrote re: Nested declarative data binding in ASP.NET 2.0
on 11-13-2007 1:38 AM
I did not read all the comments, but it seems to me that you can not use that kind of binding without selecting you ID-s from database with aliases when they do not match. Another thing is that I use uniqueidentfiers and suggest everyone to change type to string when tring to update row where it is as foreign key. One way to show or get foreign key that is declared as Type="Object" is to delete Type declaration at all.

I spent a day struggeling with code, beacause my filsophy has been so far - no coding in HTML side. I wrote everyting in class before and it works well. This Good Example made me changes my mind. In some cases this is time winning approach!!!!!!
mac wrote re: Nested declarative data binding in ASP.NET 2.0
on 02-13-2008 12:10 AM
how to click on + sign on gridview to expand gridview in asp.net 2.0
Dale Burrell wrote re: Nested declarative data binding in ASP.NET 2.0
on 06-25-2008 12:30 AM
I also am enjoying this databinding model, question, do you know how to databind another gridview within each row of the gridview using a datasource which requires a parameter based on the outer gridview row data?
Nate Adams wrote re: Nested declarative data binding in ASP.NET 2.0
on 10-10-2008 8:49 AM

I was also getting the "For handling Databinding methods such as Eval(), XPath(), and Bind() can only be used in the context of a databound control" exception.  What fixed it for me was enabling ViewState.  With ViewState enabled I no longer get the error.  If it's disabled I get it.

Hope this helps.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?