ASP.NET MVC Domain Driven Design Deux

Series Navigation

  1. Setup
  2. Mapping and Entity Equality

Entity Equality

We’ve got some more housekeeping to do on our Entity object before proceeding.  Because entities are comparable based upon Id, a standard use of the == operator will produce undesirable results. When using an ORM, this is particularly important. You will often have instances of objects, especially objects in child relationships, that are the same as, but not reference equal to, one another.

Test Cases for Desired Entity Equality Behavior

I’m a believer in documentation via test cases, so here are my test cases that demonstrate the desired behavior:


[TestFixture]
public class EntityEqualityContext
{
[Test]
public void TwoTransientEntitiesShouldNotBeEqual()
{
var entity1 = new TestEntity();
var entity2 = new TestEntity();

Assert.AreNotEqual(entity1, entity2, “Different transient entities should not be equal.”);
}

[Test]
public void TwoReferencesToSameTransientEntityShouldBeEqual()
{
var entity1 = new TestEntity();
var entity2 = entity1;

Assert.AreEqual(entity1, entity2, “Two references to the same transient entity should be equal.”);
}

[Test]
public void EntitiesWithSameIdShouldBeEqual()
{
var entity1 = new TestEntity();
var entity2 = new TestEntity();

entity1.SetId(1);
entity2.SetId(1);

Assert.AreEqual(entity1, entity2, “Entities with same id should be equal.”);
}

[Test]
public void EntitiesWithDifferentIdShouldNotBeEqual()
{
var entity1 = new TestEntity();
var entity2 = new TestEntity();

entity1.SetId(1);
entity2.SetId(2);

Assert.AreNotEqual(entity1, entity2, “Entities with different ids should not be equal.”);
}

[Test]
public void EntityShouldNotEqualTransientEntity()
{
var entity1 = new TestEntity();
entity1.SetId(1);

var entity2 = new TestEntity();

Assert.AreNotEqual(entity1, entity2, “Entity and transient entity should not be equal.”);
}

[Test]
public void EntitiesWithSameIdButDifferentTypesShouldNotBeEqual()
{
var entity1 = new TestEntity();
var entity2 = new OtherEntity();

entity1.SetId(1);
entity2.SetId(1);

Assert.AreNotEqual(entity1, entity2, “Entities of different types should not be equal even if they have the same id.”);
}

///

/// A test entity class created so that we can excercise
/// the functionality of the entity equaltiy members.
///

 

public class TestEntity : Entity
{
private int id;

public override int Id
{
get { return id; }
}

public void SetId(int id)
{
this.id = id;
}
}

public class OtherEntity : Entity
{
private int id;

public override int Id
{
get { return id; }
}

public void SetId(int id)
{
this.id = id;
}
}
}

Summarized, these tests say that entities with id of “0” should only be equal if they are the same object.  This is reference equality.  Entities with an id should be equal if their ids are equal and they are of the same type.  This is identity equality.

Entity with Equaltiy Members


public abstract class Entity: IEquatable
{
private int id;
private int transientHashCode;

public virtual int Id { get { return id; } }

public override bool Equals(object obj)
{
return Equals(obj as Entity);
}

public virtual bool Equals(Entity obj)
{
if (obj == null) return false;

if (IsTransient)
{
return ReferenceEquals(this, obj);
}

return obj.Id == Id && obj.GetType() == GetType();
}

public override int GetHashCode()
{
if (IsTransient)
{
if (transientHashCode == 0)
{
transientHashCode = base.GetHashCode();
}
return transientHashCode;
}
return id;
}

private bool IsTransient
{
get { return Id == 0; }
}
}

For more information about why insuring that your entity equality is in order, please see The NHibernate FAQ.

As this is a blog example, we will need a class to represent posts.

The Post Class


public class Post : Entity, IEquatable
{
public virtual string Title { get; set; }

public virtual string Body { get; set; }

///

/// Indicates whether the current object is equal to another object of the same type.
///

 

///
/// true if the current object is equal to the parameter; otherwise, false.
///
////// An object to compare with this object.
/// public virtual bool Equals(Post other)
{
return base.Equals(other);
}
}

In the future, we will add other properties like a collection of comments.  For now, we will keep it simple in order to demonstrate the fluent mapping.

What’s the Deal with the Virtual Members

NHibernate makes use of proxies for advanced features like lazy loading.  The default proxy factory uses Castle’s DynamicProxy. DynamicProxy generates inheritance based proxies; therefore, it can only work if members are marked as virtual so that it can override them.  There are other proxy choices; however, I will not be covering them. Making members virtual is a small price to pay for the convenience in my opinion.

Mapping the Post Entity to the Database

Generating the mappings for your entities could not be easier with fluent NHibernates automapper.

Here is a simple utility class to add to the Service project:


public class DomainMapper
{
private readonly IPersistenceConfigurer persistenceConfigurer;

public DomainMapper(IPersistenceConfigurer persistenceConfigurer)
{
this.persistenceConfigurer = persistenceConfigurer;
}

public void Configure(Configuration configuration)
{
Fluently.Configure(configuration).Mappings(mapping => mapping.AutoMappings.Add(GetMapper())).Database(
persistenceConfigurer).BuildSessionFactory();
}

protected virtual AutoPersistenceModel GetMapper()
{
var persistenceModel = new AutoPersistenceModel(typeof (Entity).Assembly)
.Where(type => !type.IsAbstract && typeof (Entity).IsAssignableFrom(type))
.WithConvention(convention =>
{
convention.IdConvention =
id => id.Access.AsReadOnlyPropertyThroughCamelCaseField().GeneratedBy.HiLo(100.ToString());
convention.IsBaseType = type => type == typeof (Entity);
convention.DefaultStringLength = 255;
});
return persistenceModel;
}
}

I like to use HiLo for the id convention and avoid using Native Identity.  For more information about the identity generators available using NHibernate, please see Vadi’s Rants.

The DefaultStringLength is just a personal preference.  The default is 100 if you do not specify a convention.

The IsBaseType is necessary so fluent NHibernate does not create joined table entities from everything that inherits from Entity.

In the Blog.Service.Test project, add this utility class:


public class TestDomainMapper: DomainMapper
{
public TestDomainMapper() : base(SQLiteConfiguration.Standard.InMemory().ShowSql())
{
}
}

This class simply inherits from DomainMapper and provides a SQLite in memory database as the persistence configuration.  Using an in-memory database for our tests will make them run much faster.  As the project grows, it is advisable to also run your integration tests vs the actual database that you will be using; however, running them against an in-memory database during standard development saves time and is very convenient.

In my first post, I attempted to make a thorough list of the tools that would be used in the project.  I neglected at least one though: SQLite.  I may have neglected more, so do not be surprised if we have to add more binaries throughout the series to our libraries directory.  System.Data.SQLite can be downloaded here.  Add it to trunk/libraries/sqlite and reference it from the Blog.Service.Test project.

Fluent NHibernate provides a useful helper for integration tests: PersistenceSpecification<T>.  Here is what the initial Post integration test looks like:


[TestFixture]
public class MappingIntegrationContext
{
protected ISession Session { get; private set; }

[SetUp]
public void BeforeEachTest()
{
var configuration = new Configuration();
new TestDomainMapper().Configure(configuration);

var sessionFactory = configuration.BuildSessionFactory();
Session = sessionFactory.OpenSession();

IDbConnection connection = Session.Connection;

string[] scripts = configuration.GenerateSchemaCreationScript(Dialect.GetDialect(configuration.Properties));
scripts.ToList().ForEach(script =>
{
var cmd = connection.CreateCommand();
cmd.CommandText = script;
cmd.ExecuteNonQuery();
});

Session.Flush();
}

[Test]
public void PostIntegrationTest()
{
new PersistenceSpecification(Session)
.CheckProperty(x => x.Title, “A Test Post”)
.CheckProperty(x => x.Body, “This is where the content of my post will be. Yay.”)
.VerifyTheMappings();
}
}

This is a good start, but obviously as our domain model grows and we need more test classes, this SetUp can be abstracted into its own base class that other test classes can inherit from.


public abstract class FixtureBase
{
[SetUp]
public void BeforeEachTest()
{
var configuration = new Configuration();
new TestDomainMapper().Configure(configuration);

var sessionFactory = configuration.BuildSessionFactory();
Session = sessionFactory.OpenSession();

IDbConnection connection = Session.Connection;

string[] scripts = configuration.GenerateSchemaCreationScript(Dialect.GetDialect(configuration.Properties));
scripts.ToList().ForEach(script =>
{
var cmd = connection.CreateCommand();
cmd.CommandText = script;
cmd.ExecuteNonQuery();
});

Session.Flush();
}

protected ISession Session { get; private set; }
}

Now the noise of the SetUp method can be taken out of the MappingIntegrationContext and we have a reusable base class for future test classes.

Implementing IRepository

Next, we will implement our repository.  NHibernate.Burrow AppBlock makes this trivial.  Reference NHibernate.Burrow.AppBlock from the Blog.Service assembly.


using System.Collections.Generic;
using Blog.Domain;
using NHibernate.Burrow.AppBlock.DAOBases;

namespace Blog.Service
{
public class Repository: GenericDAO, IRepository where T : Entity
{
///

/// Find an entity by unique identifier.
///

 

///Unique identifier of the entity. /// Entity if found, null otherwise.
public T Find(int id)
{
return base.Get(id);
}

///

/// Find all entities.
///

 

/// Sequence of entities found.
public new IEnumerable FindAll()
{
return base.FindAll();
}

///

/// Save (insert or update as appropriate) on next commit of the unit of work.
///

 

///Entity to be saved. public new void Save(T entity)
{
base.Save(entity);
}

///

/// Delete on next commit of the unit of work.
///

 

///Entity to be deleted. public new void Delete(T entity)
{
base.Delete(entity);
}
}
}

For the basic functionality, AppBlock provides everything that we need.

Update: Some of you want the sample project that I am working from, so I will add a zip of it soon.

In the next post, we will wire up the Inversion of Control in our MVC app and create a controller for Post CRUD.

kick it on DotNetKicks.com

ASP.NET MVC Domain Driven Design

Series Navigation

  1. Setup
  2. Mapping and Entity Equality

Introduction

In this series of posts, I will detail how I set up projects using the principles of domain driven design in ASP.NET MVC.

The tools and libraries we will use are as follows:

The sample app will be the canonical blog example. This series will be light on the discussion of the theory of DDD and heavy on the nuts and bolts of asp.net MVC implementation.

Project Structure

Create a new directory for the project.  I’m calling mine DDDSample. Following subversion convention, create a trunk and branches folder inside of DDDSample. Inside of trunk, create three directories: src, tools, and libraries.

The ‘src’ directory is where our code will go. The ‘tools’ directory will have NUnit and in the future, NAnt. The ‘libraries’ directory will contain the third party binaries that the project references.

Gathering the Libraries and Tools

1) Download the latest binary of fluent nhibernate from http://fluentnhibernate.org/downloads. Put the fluent nhibernate binaries in trunk/libraries/fluent-nhibernate.

2) Download NHibernate.Burrow from http://sourceforge.net/project/showfiles.php?group_id=216446. Put the burrow binaries in trunk/libraries/nhibernate.burrow. Important Note: Sometimes the latest version of NHibernate.Burrow will not have been built against the same version of NHibernate as fluent nhibernate was. Therefore, it is important that you be able to build NHibernate.Burrow, and the rest of the libraries that follow, from source.  NHibernate.Burrow is a part of NHContrib. The NHContrib svn repository is located at https://nhcontrib.svn.sourceforge.net/svnroot/nhcontrib/trunk. There is a build script in the root directory of the NHibernate.Burrow subdirectory of the project. To rebuild with a different version of NHibernate, just replace the NHibernate dll in NHibernate.Burrow/lib/NHibernate with the binary of NHibernate from the fluent nhibernate.

3) Download NUnit from http://www.nunit.org/index.php?p=download.  Get the zip distribution. Unzip the entire contents into trunk/tools/nunit. It is important that you include the full NHibernate distribution with your project so when we add an automated build tool later it will be able to run the unit tests without assuming that every developer has installed the correct version of NUnit from msi.

4) Download Moq from http://code.google.com/p/moq/downloads/list. Put the binaries in trunk/libraries/moq.

5) Download MVCContrib from http://www.codeplex.com/MVCContrib. Put the binaries in trunk/libraries/mvccontrib.

As an added measure of safety against change, I like to reference the MVC libraries themselves from binaries I store in my svn repository instead of from source.  You don’t have to do this, but if you choose to, I put System.Web.Mvc, System.Web.Routing, and System.Web.Abstractions in trunk/libraries/mvc.

Your completed project structure should look like this:

Creating the Projects

Our mvc project and class libraries will go in trunk/src.

Create an asp.net MVC project.  For this sample, I’m calling mine Blog.Web.

Create two class libraries: Blog.Domain and Blog.Service. Blog.Domain is where our POCO (Plain Old C# Objects) entities and our repository interfaces will reside. Blog.Service is where the business logic that ties the application together will live.

Reference Blog.Domain and Blog.Service from Blog.Web. Reference Blog.Domain from Blog.Service.

Create a new solution folder (right click on project, hover over add, choose new solution folder) called Test. This is where our test projects will go.

Create three class libraries: Blog.Web.Test, Blog.Domain.Test, and Blog.Service.Test.  In each of these projects reference NUnit.Framework from /trunk/tools/nunit.

More test projects may be needed later for separating unit tests from integration tests, but this is how I like to start with one unit test project for each of my projects.

The solution should now look like this:

Entity Base Class

In the domain, create an object to be the base class for entities. An entity is an object that has an identity.

namespace Blog.Domain
{
public abstract class Entity
{
private int id;

public virtual int Id { get { return id; } }
}
}

There is no way to set the id property of the Entity base class. This is by design. Our persistence layer will use NHiberante generators to generate the identity values when we save new entities to the database.

Repositories

Just what are repositories? Repositories are an abstraction of the way we manage aggregates. Repositories should correspond only to aggregate roots.  An aggregate is a cluster of objects that are treated as a unit.  The aggregate root is the root object of the unit.

In Domain Driven Design, the repository interfaces are a part of the domain, but the implementations are not.

Generic Repository of Entities

All entity repositories will need some base functionality, so we define IRepository<T> with some basic methods that we expect any repository to need.

using System.Collections.Generic;

namespace Blog.Domain
{
///

 

/// Repository of entities.
///

 

 

public interface IRepositorywhere T : Entity
{
///

 

/// Find an entity by unique identifier.
///

 

 

///Unique identifier of the entity. /// Entity if found, null otherwise.
T Find(int id);

///

 

/// Find all entities.
///

 

 

/// Sequence of entities found.
IEnumerable FindAll();

///

 

/// Save (insert or update as appropriate) on next commit of the unit of work.
///

 

 

///Entity to be saved. void Save(T entity);

///

 

/// Delete on next commit of the unit of work.
///

 

 

///Entity to be deleted. void Delete(T entity);
}
}

Conclusions

In this post we have done a lot of nuts and bolts work. The amount of setup required may seem daunting, but as stated earlier, the third party tools and libraries will pay dividends in the long-run.

In the next post, we will create a Post entity, create a concrete implementation of IRepository<Post> using NHibernate, map Post to a database using Fluent NHibernate, and test our persistence model.

kick it on DotNetKicks.com