Monday, 16 August 2010

Writing a windows phone 7 application with an Azure backend: part 2 (data access)

In the first part of this series I gave a high level overview of the architecture of the windows phone 7 application I am writing. In this post I will discuss the data access part of the application. I will use the first version of the Azure driver for Nhibernate as a foundation. This post assumes that you have a Azure account or that you have installed the development fabric. If that is not the case, you' will need to download and install it first.

Let’s start with defining a simple entity. I will reuse my favorite entity: Wine ;-)

public class Wine
{
    public virtual string Id { get; set; }
    public virtual string TenantId { get; set; }
    public virtual string Name { get; set; }
    public virtual int Price { get; set; }
    public virtual int Rating { get; set; }
    public virtual string Region { get; set; }
    public virtual string Style { get; set; }
 
    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        var candidate = obj as Wine;
        if (candidate == null) return false;
        return this.Id == candidate.Id && this.TenantId == candidate.TenantId;
    }
 
    public override int GetHashCode()
    {
        int hash = 13;
        hash = hash + (null == this.Id ? 0 : this.Id.GetHashCode());
        hash = hash + (null == this.Id ? 0 : this.TenantId.GetHashCode());
        return hash;
    }
}

and the Nhibernate mapping that goes with this entity:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="Vino.Domain"
                   namespace="Vino.Domain.Models">
  <class name="Wine" table="wines">
    <composite-id>
      <key-property name="Id" column="RowKey" />
      <key-property name="TenantId" column="PartitionKey"/>
    </composite-id>
    <property name="Price" type="System.Int32" />
    <property name="Rating" type="System.Int32" />
    <property name="Name" type="String" />
    <property name="Style" type="String" />
    <property name="Region" type="String"></property>
  </class>
</hibernate-mapping>

As Yves mentioned in his post to introduce the Nhibernate driver for Azure the identifier must be a composite key that included “RowKey” and “PartitionKey” and both of them have to be strings. It is also important to choose your partition key wisely. Tables on azure table storage are partitioned to support load balancing across storage nodes. Choose a property as partition key that groups your entitities. In my case I took the “tenant id” which means I will have a separate partition for the wines of each tenant. The remainder of the mapping and the entity itself is pretty straightforward, so let’s move on to the repository.

public class WineRepository :IWineRepository
    {
        public IEnumerable<Wine> GetAllWinesByTenant(string tenantId)
        {
            return SessionScope.CurrentSession.CreateCriteria<Wine>()
                    .Add(Restrictions.Eq("TenantId", "1"))
                    .List<Wine>();
        }
    }

The method “GetAllWinesByTenant” actually looks exactly the same as it does when you would write a method to get data out of SqlServer with NHibernate. Note however that the Azure driver for Nhibernate does not support queries without a where clause at this moment. This is by design because normally it does not make much sense to read an entire table from Azure table storage into the memory of your application. It might be added in the future though.

SessionScope is a class from the Capgemini framework. It has a CurrentSession property which gives you the current session during a WCF call. I will show you in the next post how this CurrentSession is set for each WCF call. One of the things it will certainly need is a NHibernate session factory to be able to open NHibernate sessions. You can build the NHibernate session factory as follows:

public class SessionFactoryBuilder
{
    public static ISessionFactory BuildSessionFactory()
    {
        return Fluently.Configure().Database(AzureConfiguration.TableStorage
                .ProxyFactoryFactory(typeof(ProxyFactoryFactory).AssemblyQualifiedName).ShowSql())
                .Mappings(cfg => cfg.HbmMappings.AddFromAssemblyOf<Wine>())                    
                .BuildSessionFactory();
    }
}

After this post you should be able to write some unit tests that store and retrieve data from the Azure table storage. To do that you will need an entity, a NHibernate mapping file and a configured session factory to be able to open NHibernate sessions.

In the next post I will explain how we can bring WCF in the mix and use the repositories from within a WCF service.

1 comment:

Microsoft Office said...

Microsoft Outlook 2010 to help control email volume, you can find the desired content, and perform operations at the appropriate time and location. Outlook 2010 download needs no introduction since it is the industry standard.