(Or, what the tortoise said to Achilles on declarative data sources)
I've been having this internal struggle with declarative data sources recently, and I thought it might help to air my thoughts and perhaps garner some feedback from some of you. [With apologies to Zeno, Lewis Carroll, and Douglas Hofstadter...
Achilles: The declarative data source controls in ASP.NET 2.0 greatly simplify the process of binding data to a control (look ma, no code!).
<asp:GridView ID="GridView2" runat="server"
AutoGenerateColumns="True" DataKeyNames="au_id"
DataSourceID="authorsDataSource" />
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:pubsConnectionString %>"
SelectCommand="SELECT * FROM [authors]" />
Tortoise: That does seem concise, but by default the SqlDataSource maps onto a DataSet-based retrieval, which didn't really involve much code in the first place.
<asp:GridView ID="GridView2" runat="server"
DataKeyNames="au_id" />
// in code behind
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Authors",
ConfigurationManager.ConnectionStrings[
"pubsConnectionString"].ConnectionString);
da.Fill(ds);
GridView2.DataSource = ds;
GridView2.DataBind();
}
}
Achilles: But I can switch my data source control to use IDataReader-based retrieval of data instead of a DataSet with one property!
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="True" DataKeyNames="au_id"
DataSourceID="authorsDataSource" />
<asp:SqlDataSource ID="authorsDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:pubsConnectionString %>"
SelectCommand="SELECT * FROM [authors]"
DataSourceMode="DataReader" />
Tortoise: Ok, but programmatically using IDataReader didn't really involve much code either.
<asp:GridView ID="GridView2" runat="server"
DataKeyNames="au_id" />
// in code behind
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string dsn = ConfigurationManager.ConnectionStrings[
"pubsConnectionString"].ConnectionString;
using (SqlConnection conn = new SqlConnection(dsn))
using (SqlCommand cmd =
new SqlCommand("SELECT * FROM Authors", conn))
{
conn.Open();
SqlDataReader r = cmd.ExecuteReader();
GridView2.DataSource = r;
GridView2.DataBind();
}
}
}
Alright, Achilles, I'll concede that being able to perform declarative datareader-based retrieval is kind of nice. BUT - if you want to enable sorting or paging, you can't use the datareader mode.
Achilles: Let's talk about paging and sorting. If I switch my declarative data source back to DataSet-mode retrieval, I can, with the simple click of a mouse (or by adding two additional properties), enable both sorting and paging - and it just works!
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="True" DataKeyNames="au_id"
DataSourceID="authorsDataSource"
AllowPaging="True" AllowSorting="True" />
<asp:SqlDataSource ID="authorsDataSource" runat="server"
ConnectionString="<%$ ConnectionStrings:pubsConnectionString %>"
SelectCommand="SELECT * FROM [authors]"
DataSourceMode="DataReader" />
Tortoise: Ok, I admit that does save some code, for to do the equivalent I must not only enable the sorting and paging features of the GridView, but I must also implement handlers in response to the sorting and paging events of the GridView to properly sort and return the correct rows.
<asp:GridView ID="GridView2" runat="server" AllowPaging="True"
AllowSorting="True"
OnPageIndexChanging="GridView2_PageIndexChanging"
OnSorting="GridView2_Sorting"
DataKeyNames="au_id" />
// in code behind
void BindAuthorsGrid()
{
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Authors",
ConfigurationManager.ConnectionStrings[
"pubsConnectionString"].ConnectionString);
da.Fill(ds);
DataView dv;
if (ViewState["sortExpr"] != null)
{
dv = new DataView(ds.Tables[0]);
dv.Sort = (string)ViewState["sortExpr"];
}
else
dv = ds.Tables[0].DefaultView;
GridView2.DataSource = dv;
GridView2.DataBind();
}
protected void Page_Load(object sender, EventArgs e)
{
BindAuthorsGrid();
}
protected void GridView2_Sorting(object sender,
GridViewSortEventArgs e)
{
// cache in ViewState - for some reason the
// GridView's SortExpression
// and SortDirection properties do not retain this information
//
ViewState["sortExpr"] = e.SortExpression +
(e.SortDirection == SortDirection.Ascending ?
" ASC" : " DESC");
BindAuthorsGrid();
}
protected void GridView2_PageIndexChanging(object sender,
GridViewPageEventArgs e)
{
GridView2.PageIndex = e.NewPageIndex;
BindAuthorsGrid();
}
Achilles: Aha - so you admit that declarative data sources are a great advance and will save developers countless hours of agonizing over code!
Tortoise: You're putting words in my mouth. I said that for me 'to do the equivalent' would take some code, but I'm not convinced I would want to do that in many cases. For one, all of these features are relying on capabilities of the DataSet - in fact, it strikes me that declarative data sources are merely a wizard-friendly way to integrate DataSet features into the control binding process.
Achilles: Well, even if that's true, there's something to be said for that.
Tortoise: True, it is a convenient way to expose DataSet features which may be the right choice in many cases, but it also strikes me as potentially dangerous.
Achilles: Dangerous? Surely you exaggerate, what could be dangerous about the DataSet?
Tortoise: Well, for one, the sorting feature of the DataSet is abysmally slow for any significant amount of data (same goes for the filtering capability). Databases are much more efficient at both filtering and sorting data, and their capabilities should be leveraged. Secondly, the paging mechanism you demonstrated loads all of the data into the DataSet and then the GridView simply indexes into the DataSet to display the correct rows. For large sets of data, this is potentially a huge amount of overhead.
Achilles: Well, you can always use the SortParameterName property of the data source and switch it to DataReader mode which would let you use your precious database to do the sorting.
Tortoise: You know, I tried that, and it worked fine, but I found that I was writing nearly as much code as I had done before without the data source. In addition to writing nearly the same amount of code, I had to deal with the additional abstraction of the data source which at that point seemed superfluous.
Achilles: You know, there are some other cool features of the data source controls that you haven't mentioned - like caching and conflict detection during updates.
Tortoise: Are they also dependent on the DataSet to function properly?
Achilles: Well, erm... yeah.
Tortoise: My point stands. The declarative data source model is a mechanism for exposing DataSet functionality in a declarative way for the wizard and designer.
Achilles: What about the ObjectDataSource - that can be bound to anything!
Tortoise: Let's talk about that...
<to be continued...>
Posted
Apr 05 2005, 08:50 AM
by
fritz-onion