AWSandbox


There is not a lot of good examples for NHibernate mapping. I struggle to figure this stuff out but once I get it it always seems so simple.

In the AdventureWorks Person schema there is a foreign key reference from the StateProvince table to the CountryRegion table by the CountryRegionCode column. Here is the StateProvince class code:

namespace AWSandbox.Domain.Person {
public class StateProvince {
private int id;
private string stateProvinceCode;
private CountryRegion countryRegion;
private bool isOnlyStateProvinceFlag;
private string name;
private int territoryId;

public int Id {
get { return id; }
set { id = value; }
}

public string StateProvinceCode {
get { return stateProvinceCode; }
set { stateProvinceCode = value; }
}

public CountryRegion CountryRegion {
get { return countryRegion; }
set { countryRegion = value; }
}

public bool IsOnlyStateProvinceFlag {
get { return isOnlyStateProvinceFlag; }
set { isOnlyStateProvinceFlag = value; }
}

public string Name {
get { return name; }
set { name = value; }
}

public int TerritoryId {
get { return territoryId; }
set { territoryId = value; }
}
public string CountryRegionName {
get { return CountryRegion.Name; }
}
}
}

Notice that the CountryRegionName only has a getter and that it really is using the CountryRegion class to return the Name property.

Here is the mapping:

<?xml version=”1.0″ encoding=”utf-8″ ?>
<hibernate-mapping xmlns=”urn:nhibernate-mapping-2.2″ assembly=”AWSandbox.Domain” namespace=”AWSandbox.Domain”>
<class name=”AWSandbox.Domain.Person.StateProvince, AWSandbox.Domain” table=”Person.StateProvince” lazy=”false”>
<id name=”Id” column=”StateProvinceID”>
<generator class=”native”/>
</id>
<property name=”StateProvinceCode” column=”StateProvinceCode” type=”string” />
<property name=”IsOnlyStateProvinceFlag” column=”IsOnlyStateProvinceFlag” type=”Boolean” />
<property name=”Name” column=”Name” type=”string” />
<many-to-one name=”CountryRegion”class=”AWSandbox.Domain.Person.CountryRegion”
column=”CountryRegionCode” cascade=”all” />
</class>
</hibernate-mapping>

I’m using the <many-to-one> mapping to include the CountryRegion data with the StateProvince data. Now this is how you would display the data in a GridView:

<asp:GridView ID=”StateProvinceGridView” AutoGenerateColumns=”false” runat=”server”>
<Columns>
<asp:BoundField DataField=”Id” HeaderText=”ID”/>
<asp:BoundField DataField=”StateProvinceCode” HeaderText=”Code” />
<asp:BoundField DataField=”IsOnlyStateProvinceFlag” HeaderText=”Flag” />
<asp:BoundField DataField=”Name” HeaderText=”Name” />
<asp:BoundField DataField=”CountryRegionName” HeaderText=”Country Region” />
</Columns>
</asp:GridView>

I’m assigning an IList<StateProvince> objects to the GridView.DataSource and calling GridView.DataBind() to populate the grid. I’m also using the MVP (model view presenter) design pattern to move the data out of the database, through my domain, presentation and service layers and arriving in tabular format on the web page.

I haven’t spent much time on the AWSandbox solution for the last couple of weeks. I got busy working on the prototypes for work and didn’t get back to this until Friday night. With a little time I’ve got it working. By working I mean that I can query the database via NHibernate and populate an IList<T> of objects. Using a service layer class and a presentation layer class I can display the queried data in a GridView on a web page.

It isn’t a lot of functionality but it does move data from the database to a browser. The best part of this is it is easy to add more web pages and create additional presentation and service classes to support it. It is almost like cutting and pasting without the maintenance headaches.

Now that I have the basic data movement in place I will start to flesh out this solution to do more things. One of the first areas I’m going to expand is the Employee web page. This will tie together Employee information with Address and Manager information. To get this working I will have to markup the mapping between the Address, StateProvince and Employee tables with their classes.

In the “Displaying data in the GridView” diary I showed how to get the vendor information out of the database and place it in a GridView control. However my Vendor class contains

IList vendorAddressList = new ArrayList();

that is populated via NHibernate. It took me a couple of hours to figure out how to display the address information in the GridView. They are a couple of different approaches to this but I just want to display it as simple as possible. I choose to use the TemplateField to do this.

<asp:TemplateField HeaderText=”Address”>
<ItemTemplate>
<%# ((Address)(((Vendor)Container.DataItem).VendorAddressList)[0]).AddressLine1 %> <br />
<%# ((Address)(((Vendor)Container.DataItem).VendorAddressList)[0]).City %> <%# ((Address)(((Vendor)Container.DataItem).VendorAddressList)[0]).PostalCode %>
</ItemTemplate>
</asp:TemplateField>

The trick was to get the casting right. In order for this to work you use the Container.DataItem property. The first thing is to cast it to the Vendor class: ((Vendor)Container.DataItem). So now I have my Vendor class. Next I need to get the VendorAddressList which is an IList collection:

(((Vendor)Container.DataItem).VendorAddressList)

This needs to be cast to the Address class and because it is a collection I need the first element of the collection.

((Address)(((Vendor)Container.DataItem).VendorAddressList)[0])

Now I can get any property of the Address class and display it.

((Address)(((Vendor)Container.DataItem).VendorAddressList)[0]).AddressLine1

That sure is a lot of casting to get the street address to display.

Today I made some progress and learned how to display data in a GridView using the MVP design pattern. The displaying of the data is simple without any fancy formatting and the data is pulled from the AdventureWorks Vendor table in the Purchasing schema. Below is a screenshot of the grid.

GridView

Once I got the mapping squared away the rest just fell into place. The MVP design pattern works well for this and here are the pieces I used.

AWSandbox.Domain Project

In the domain layer I created a class called Vendor. It is a simple class that I showed the code to in “AWSandbox and NHibernate mapping”.

AWSandbox.Presentation Project

In this project I create an interface called IVendorListView. Here is the code:

using System.Collections.Generic;
using AWSandbox.Domain.Purchasing;

namespace AWSandbox.Presentation.ViewInterfaces {
public interface IVendorListView {
IList<Vendor> VendorList { set; }
}
}

I also created a class called VendorListViewPresenter. Here is the code:

using AWSandbox.Presentation.ViewInterfaces;
using AWSandbox.Services.Purchasing;

namespace AWSandbox.Presentation.Purchasing {
public class VendorListViewPresenter {
private readonly IVendorListView view;
private readonly IVendorService service;

public VendorListViewPresenter(IVendorListView view) : this(view, new VendorService()) {}
public VendorListViewPresenter(IVendorListView view, IVendorService service) {
this.view = view;
this.service = service;
}

public void Initialize() {
view.VendorList = service.GetVendorList();
}
}
}

AWSandbox.Services Project

In this project I created a class called VendorService. Here is the code:

using System.Collections.Generic;
using AWSandbox.DataAccess.UnitOfWork;
using AWSandbox.Domain.DataInterface;
using AWSandbox.Domain.Purchasing;

namespace AWSandbox.Services.Purchasing {
public class VendorService : IVendorService {
private IRepositoryFactory factory;
private IVendorRepository vendorRepository;

public VendorService() {
factory = new NHibernateRepositoryFactory();
vendorRepository = factory.GetVendorRepository();
}

public IList<Vendor> GetVendorList() {
IList<Vendor> vendorList = vendorRepository.GetAll();
return vendorList;
}
}
}

These are the pieces you need to display the data on a WebForm.

Web Page

This is how I configured the GridView on the WebForm page (.aspx)

<asp:GridView ID=”VendorListGridView” runat=”server” AutoGenerateColumns=”false” BorderStyle=”solid”>
<Columns>
<asp:BoundField DataField=”AccountNumber” HeaderText=”Account Number” />
<asp:BoundField DataField=”Name” HeaderText=”Vendor Name” />
<asp:BoundField DataField=”CreditRating” HeaderText=”Credit Rating” />
<asp:BoundField DataField=”PreferredVendorStatus” HeaderText=”Status” />
<asp:BoundField DataField=”ActiveFlag” HeaderText=”Active” />
<asp:BoundField DataField=”PurchasingWebServiceURL” HeaderText=”Vendor URL” />
</Columns>
</asp:GridView>

Here is the code behind for the WebForm page.

using System.Collections.Generic;
using AWSandbox.Domain.Purchasing;
using AWSandbox.Presentation.Purchasing;
using AWSandbox.Presentation.ViewInterfaces;

public partial class WebPages_Purchasing_VendorList : System.Web.UI.Page, IVendorListView {
private VendorListViewPresenter presenter;

protected override void OnInit(System.EventArgs eArgs) {
base.OnInit(eArgs);
presenter = new VendorListViewPresenter(this);
}

protected override void OnLoad(System.EventArgs eArgs) {
base.OnLoad(eArgs);
if (!IsPostBack) {
presenter.Initialize();
}
}

public IList<Vendor> VendorList {
set {
VendorListGridView.DataSource = value;
VendorListGridView.DataBind();
}
}
}

That’s all there is to it. Notice that on the code behind I have the IVendorListView interface. In the OnInit method I instantiate the presenter. In the OnLoad I call the Initialize method of the presenter. The last little piece is I set the VendorList to the GridView’s DataSource (this is an IList) and call the GridView’s DataBind.

There are not a lot of examples of NHibernate mapping outside of the NHibernate - Relational Persistence for Idiomatic .NET document. There is a simple example by Paul Wilson and another by Scott Bellware but not much more.

I’m trying to figure out how to map the Vendor -> VendorAddress and Vendor -> VendorContact tables in the Purchasing schema of AdventureWorks. This is the mapping that finally worked:

<hibernate-mapping xmlns=”urn:nhibernate-mapping-2.2″ assembly=”AWSandbox.Domain” namespace=”AWSandbox.Domain”>
<class name=”AWSandbox.Domain.Purchasing.Vendor, AWSandbox.Domain” table=”Purchasing.Vendor” lazy=”false”>
<id name=”VendorId” column=”VendorID”>
<generator class=”native”/>
</id>
<property name=”AccountNumber” column=”AccountNumber” type=”string” />
<property name=”Name” column=”Name” type=”string” />
<property name=”CreditRating” column=”CreditRating” type=”int” />
<property name=”PreferredVendorStatus” column=”PreferredVendorStatus” type=”int” />
<property name=”ActiveFlag” column=”ActiveFlag” type=”int” />
<property name=”PurchasingWebServiceURL” column=”PurchasingWebServiceURL” type=”string” />
<bag name=”VendorAddressList” table=”Purchasing.VendorAddress” cascade=”none” access=”nosetter.camelcase” lazy=”false” inverse=”true”>
<key column=”VendorID” />
<many-to-many column=”AddressID” class=”AWSandbox.Domain.Person.Address, AWSandbox.Domain” />
</bag>
<bag name=”VendorContactList” table=”Purchasing.VendorContact” cascade=”none” access=”nosetter.camelcase” lazy=”false” inverse=”true”>
<key column=”VendorID” />
<many-to-many column=”ContactID” class=”AWSandbox.Domain.Person.Contact, AWSandbox.Domain” />
</bag>
</class>
</hibernate-mapping>

Sorry for the formatting but this is taken from the Wilson example. This will return the Vendor records with the VendorAddress -> Address records and the VendorContact -> Contact records.

The Vendor class looks like this:

namespace AWSandbox.Domain.Purchasing {
public class Vendor {
#region Members - private

private int vendorId;
private string accountNumber;
private string name;
private int creditRating;
private int preferredVendorStatus;
private int activeFlag;
private string purchasingWebServiceURL;
private IList vendorAddressList = new ArrayList();
private IList vendorContactList = new ArrayList();

#endregion

#region Properties

public int VendorId {
get { return vendorId; }
set { vendorId = value; }
}

public string AccountNumber {
get { return accountNumber; }
set { accountNumber = value; }
}

public string Name {
get { return name; }
set { name = value; }
}

public int CreditRating {
get { return creditRating; }
set { creditRating = value; }
}

public int PreferredVendorStatus {
get { return preferredVendorStatus; }
set { preferredVendorStatus = value; }
}

public int ActiveFlag {
get { return activeFlag; }
set { activeFlag = value; }
}

public string PurchasingWebServiceURL {
get { return purchasingWebServiceURL; }
set { purchasingWebServiceURL = value; }
}

public IList VendorAddressList {
get { return vendorAddressList; }
set { vendorAddressList = value; }
}

public IList VendorContactList {
get { return vendorContactList; }
set { vendorContactList = value; }
}
#endregion
}
}

This works but there may be other and even better ways of doing this.

It has been about two weeks since I have posted anything about the AWSandbox project because I have been working on an architectural prototype for work. The prototype uses the similar to architecture AWSandbox so it has some experiential benefits.

My work prototype uses a different domain and goes against a different database but the use of NHibernate, NUnit, NAnt and Rhino Mocks is the same. The work prototype is a lot more complex than the AWSandbox project but I’ve come to a point in it that I need to experiment with UI approaches and having a simpler project like AWSandbox will be helpful.

So I’m going to start working on the AWSandbox to gain insight into UI architectural choices that I can leverage in the work prototype. The issue I’m wrestling with currently is how to move information out of the database and display it in a grid. I’m going to be using the MVP design pattern but I haven’t worked with a grid. Up to this point I have only used dropdown listboxes and textboxes to display database information. The grid is a little more challenging.

Another major difference with the work prototype is I’m using continuous integration to build and test the source code with each check in. I won’t do that with the AWSandbox project. I will have a build file and unit tests but I will do them from within Visual Studio as opposed to using a build machine and a source machine. Maybe at a later date I will write about my continuous integration experiences.

So I’ve now spent some time with both of these approaches. I have decided to go with the .hbm.xml files for managing the O/R Mapping. While I like the idea of both of these approaches I found that it is not making the metadata mapping that much easier.

ActiveRecord requires adding more reference DLLs and even though the mark up is done with attributes in the class code it still needs to be there. It seems to move away from the KISS question my co-worker brought up. If I was going to use more of the Castle project I would go with it. The Castle project is dedicated to simplifying enterprise and web applications but in my case it seems to be overkill.

With NHibernate.Mapping.Attributes I found that it doesn’t take the DLLs that ActiveRecord requires. It only takes NHibernate.Mapping.Attributes.dll. It does take some additional configuration code to take the attributes that are used in the class code and serialize them into an input stream. Again I like the idea of using attributes in the class code but the fact that I have to configure an input stream and then have it serialize what would be found in a .hbm.xml file just doesn’t seem to gain me a lot.

So I’m going with the .hbm.xml file route on the KISS principle. No matter what I use I still have to do the O/R Mapping. I like the idea of using attributes but in this I’m going to stick with using .hbm.xml.

I was in a discussion with our architect today and he is wanting to move to using some of the Castle Project components as we move forward in converting from a legacy ASP/Javascript application to a ASP.NET 2.0 application.

I was mentioning this to one of my c0-workers and his question was, “Why aren’t we using just ADO.NET and not all of this extra stuff that no one knows how it works?”.

Good question and in some ways I have to agree with him about not making this so difficult that the rest of the team members have trouble figuring it out.

For the AWSandbox solution I’m going to move to using ActiveRecord for the persistence. I know this is not the way I thought to go just a couple of days ago. But the best way I know how to answer the KISS question is to learn about the non-KISS approach. Without some experience using both approaches I won’t be able to argue one way or the other intelligently.

So I’m going to start using ActiveRecord. ActiveRecord is built with NHibernate and uses an attribute-based mapping and not XML files.

Data access is going to be via NHibernate. One of the design patterns from Martin Fowler’s “Patterns of Enterprise Application Architecture” is called Metadata Mapping. NHibernate uses O/R Mapping so this pattern is what is needed. With NHibernate 1.2 you can do the O/R Mapping using NHibernate.Mapping.Attributes. This is new to the 1.2 version.

NHibernate supports the ICriteria interface so another design pattern that can be used is the Query Object. This pattern will nicely encapsulate the database query. To fully decouple the domain objects from the data mapping layers I will use the Repository design pattern.

These are the three Object-Relation Metadata Mapping patterns described in Fowler’s book. While this is used for enterprise size databases and it is probably overkill for the AdventureWorks database, this is the approach I’ll use for creating a domain that is PI (Persistence Ignorance).

With two domain groups starting to have objects in them I’m getting anxious to do something with them. I’m still putting off the persistence classes even though I keep thinking about them. I know that I want Services to use the domain objects to provide a collection of domain objects or single domain object. I added some finer granularity to the domain project with Person and Purchasing. I’m going to carry this over to the Services project too.

So I again begin with the Services.Test project and added the Person.Test class to it. I’m going to use the same technique of creating tests that require a PersonServices class and add it as an internal class. Once I get the basics successfully testing I’m going to move the internal class over to the Services project. At this time all I care about is whether or not I can get collections or single instances of the PersonAddress and PersonContact classes. All I’m doing is creating empty instances of these classes from an ID value or a GUID value. The same goes for the collections. All I’m doing is creating generic IList of these classes. This will allow me to start adding more and more functionality and making sure that these collections or instances are created successfully. Failures will return null values and the tests will catch them. Here is the PersonServicesTest:

using System.Collections.Generic;
using AWSandbox.Domain.Person;
using AWSandbox.Services.Person;
using NUnit.Framework;
using Rhino.Mocks;

namespace AWSandbox.Services.Test.Person.Test {
[TestFixture]
public class PersonServicesTest {
private MockRepository mock;
private PersonServices ps = null;
private int id = 0;
[SetUp]
public void SetUp() {
mock = new MockRepository();
ps = new PersonServices();
}

[Test]
public void ShouldCreatePersonServiceTest() {
Assert.IsNotNull(ps);
}

[Test]
public void ShouldGetPersonAddressListTest() {
IList<PersonAddress> list = ps.getPersonAddressList();
Assert.IsNotNull(list);
}
[Test]
public void ShouldGetPersonContactListTest() {
IList<PersonContact> list = ps.getPersonContactList();
Assert.IsNotNull(list);
}
[Test]
public void ShouldGetPersonAddressByIdTest() {
PersonAddress pa = ps.getPersonAddressById(id);
Assert.IsNotNull(pa);
}
[Test]
public void ShouldGetPersonContactByIdTest() {
PersonContact pc = ps.getPersonContactById(id);
Assert.IsNotNull(pc);
}

[Test]
public void ShouldGetPersonAddressByGuidTest() {
PersonAddress pa = ps.getPersonAddressByGuid(”");
Assert.IsNotNull(pa);
}

[Test]
public void ShouldGetPersonContactByGuidTest() {
PersonContact pc = ps.getPersonContactByGuid(”");
Assert.IsNotNull(pc);
}
[TearDown]
public void TearDown() {
mock.ReplayAll();
}
}
}

I’m keeping the Rhino mocks code in here because when I start adding functionality I’m going to need to test it. I also added PurchasingServices classes and tests that create at this point a Vendor collection or instance. It looks pretty much like the above code.

Adding the Services and Services.Test projects required me to add them AWSandbox.build file. As the solution starts to get bigger it becomes much faster to compile the application outside of VS2005. As I add classes to projects I have to add them to the NAnt script.

Next Page »