Tuesday, January 25, 2005 - Posts

Microsoft Architecture Journal 4 Out Now

From the Blog Clippings Category The Microsoft Architects Journal 4 is now available here as a pdf. The table of contents lists these articles.
  • Choosing the Right Presentation Layer Architecture
  • Information Bridge Framework
  • Benchmarking a Transaction Engine
  • Enterprise Architecture Alignment Heuristics
  • Razorbills
  • Next Generation Tools for OOD
with 1 Comments

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.

with 2 Comments