posted on Tuesday, January 25, 2005 12:05 PM
by
mlorengo
Adapting The Virtual Cellar Domain Objects to .Net using the ObjectDataSource
I've been ever so slowly adding new .Net 2.0 features to the Virtual Cellar
code. The most apparent changes are driven by the use of ASP.Net 2.0 features,
most notably the GridView control and the new suite of DataSource controls.
I wasn't too happy to see the introduction of the SqlDataSource control, I
thought it just encouraged bad layering practices, allowing the population of
the new Data Controls through straight ahead SQL statements. Sure, I could setup
a stored procedure and call that with the SqlDataSource control, but I was still
programming to the database. It just felt wrong.
So, I was rather dejected, I really wanted to use the new GridView control in
my application. That's when I found out about the ObjectDataSource control.
There's a great
MSDNTV Episode that talks about this. This was the perfect solution. I would
be able to use my Domain Objects directly with the GridView control. There was
only one problem, the object used as the data source needed to have a default
contructor.
As you may (or may not) remember, I based the Virtual Cellar model on the
Repository Pattern. Unfortunately, I based my domain objects on a base class,
RepositoryItemBase, that does not allow for a default constructor, instead it
includes a reference to the RepositoryFactory, should any of the classes such as
the Wine class need to expose properties of another aggregate root (Producer).
Part of this was to get around the problem of having a reference to the
ProducerRepository in the Wine class, instead a request would be made to the
RepositoryFactory to generate a Factory instance (in this case a ProducerFactory)
for re-hydrating the Producer class from the ProducerRepository, from
information stored in the Wine class.
The short answer, is that I am unable to use the ProducerRepository as an
ObjectDataSource, because it does not support a parameterless default
constructor. So my immediate fix is to create an adapter class
ProducerDataSource that wraps the functionality of the ProducerRepository, yet
has a parameterless default constructor to satisfy the requirements of the
ObjectDataSource.
Here's a shot of the class diagram modeled in the new VS2005 diagram tool.

As you can see, I've defined only one method "SelectByName" for the purpose
of my testing. The method takes a string that is used for matching on the
Producer.Name field. It simply calls the QueryByName method in the repository,
which maps to the GetByNameAsDataReader in the IProducerProvider interface. This
example is using the OleDbProducerProvider for the purposes of acting on an
OleDb data source.
I've used another 2.0 feature (Generics) to return a IList<Producer> from the
ProducerDataSource.SelectByName method. The GridView can now take this IList<Producer>
and map it to grid columns.
Here's a quick code excerpt that illustrates the ProducerDataSource object
and the ProducerGridView interaction.
<form id="form1" runat="server">
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
<asp:Button ID="Button1" runat="server" Text="Button" />
<asp:GridView ID="ProducerGridView" runat="server" DataSourceID="ProducerDataSource" AutoGenerateColumns="False">
<Columns>
<asp:BoundField HeaderText="Id" DataField="Id" SortExpression="Id"></asp:BoundField>
<asp:BoundField HeaderText="Name" DataField="Name" SortExpression="Name"></asp:BoundField>
<asp:BoundField HeaderText="Address" DataField="Address" SortExpression="Address"></asp:BoundField>
<asp:BoundField HeaderText="City" DataField="City" SortExpression="City"></asp:BoundField>
<asp:BoundField HeaderText="State" DataField="State" SortExpression="State"></asp:BoundField>
<asp:BoundField HeaderText="Country" DataField="Country" SortExpression="Country"></asp:BoundField>
<asp:BoundField HeaderText="PostalCode" DataField="PostalCode" SortExpression="PostalCode"></asp:BoundField>
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ProducerDataSource" runat="server" TypeName="Lorengo.VirtualCellar.Business.ProducerDataSource"
SelectMethod="SelectByName">
<SelectParameters>
<asp:ControlParameter Name="name" ControlID="TextBox1" PropertyName="Text" DefaultValue="Robert Mondavi"
Type="String" />
</SelectParameters>
</asp:ObjectDataSource>
</form>
|
You'll notice that the ObjectDataSource has a SelectMethod attribute which
maps to the SelectByName method on my ProducerDataSource. Also the <SelectParameters>
node maps the TextBox1 controls Text property to the "name" parameter of the
method. This allows the user to enter a value in the text box to search for.
The resulting pages looks like this

Unfortunately, the page does the query when it is loaded, so the DefaultValue
attribute of "Robert Mondavi" is used and displayed. What I would like to do is
prevent the initial lookup from occurring, presenting the user with just a
search box and button. Does anybody know of hand how to accomplish this?
In the meantime, I'm going to revisit my Repository / DataSource classes and
see if I can't come up with a better solution. Now that I know there is an easy
way to utilize my business objects with the data controls, I'm excited.