|
Build More Robust Databinding Apps (Continued)
ASP.NET 2.0 introduces a new way to bind to application data: the ObjectDataSource control. This control mitigates the two-tier design of the SqlDataSource approach. The ObjectDataSource control can bind to custom methods defined in a business/data access layer component of the application, while still taking advantage of declarative configuration. For example, use this code to configure the Web page with the new ObjectDataSource control:
<asp:ObjectDataSource ID="customersObjectDataSource"
runat="server"
SelectMethod="GetAllCustomers"
TypeName="DataBinding.ServiceLayer.SimpleCustomerTask">
</asp:ObjectDataSource>
This approach looks good initially, but it also has underlying issues. The view no longer needs to know anything about connection strings or the table that the data comes from. Unfortunately, that gain is offset by the fact that this approach uses string literals to define the type that exposes the binding method and the name of the binding method itself. If any one of these change and you don't update the necessary string literals, then you still must run the application before you can discover errors in your app.
.NET lets you bind to arbitrary collections of objects, as opposed to data tables and XML only. This means that you can set the DataSource property of any of the databinding controls to an IList of your own domain-specific object.
Begin by creating a Customer class that includes all of the attributes you care to know about a customer; the example assumes that this class is an object representation of a Customer row in the database (see Listing 2).
Map Your Data
So far, you have a custom class. Next, you need to get data from the database to "map" into this class. A data mapper does the trick nicely. Add a CustomerMapper class into the DataAccess Layer. Its responsibility is to retrieve a set of customer rows from the database and convert them into the appropriate customer objects. This simplified implementation of the Mapper Design Pattern is sufficient to get the point across (see Listing 3).
This implementation is a first step to achieving automated application failure recognition. Ultimately, you want to be able to identify as many errors as possible without running the application manually. Let's focus on how you could capture errors caused by changes made to the Customers table. Do this by writing a test for the CustomerMapper class (see Listing 4). Pay attention to the TransactionScope object, which is new to .NET Framework 2.0. The test method uses it to ensure that you don't need to roll back any changes made to the database manually after test execution. By not calling Complete on the TransactionScope, any effects the test has on the database are reversed when the test ends.
The approach I'm describing now is quite a bit more work up front than the codeless databinding I covered at the outset. Let's look at what this effort buys you. The CustomerMapper is now responsible for retrieving data from the Customers table, as well as creating the necessary domain objects from it. This means the "ShouldBeAbleToGetAllCustomers" test will now fail for both scenarios, if either the table name or the name of one of the columns has changed. You can take advantage of new features in Visual Studio 2005 to run these automated tests from inside the IDE or from the command line. This eliminates the initial issue of needing to run the application to catch these errors—a tremendous benefit.
Next, wire the desired data back up to the UI:
public partial class DeclarativeWithNoDataSourceControl : Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (! IsPostBack)
{
BindCustomersGrid();
}
}
private void BindCustomersGrid()
{
this.customersGridView.DataSource = new
CustomerTask().GetAllCustomers();
this.customersGridView.DataBind();
}
}
This code invokes a method on your service layer that returns a generic IList of type Customer; bind your data grid to the invoked method. The method that returns the data to you is fully tested, as is the underlying mapper that takes rows from the database and converts them into Customer objects.
Back to top
|