Upgrading to Sharp Architecture 1.6 and MVC 2

After a good few months in hibernation the guilt of leaving my poor pet project half-finished has finally got to me!

I was also spurred on by several new (and not so new) software releases that I would like to try out:

The first step I needed to take was to upgrade the project to use the new release of Sharp Architecture and MVC 2. The Sharp Architecture wiki has  a decent upgrade guide and I was able to get most of the way there by following this.  However there were a couple extra step I needed to take in my project:

Upgrade to Fluent NHibernate

I think my project may have been on an earlier version than that in the guide because I needed to upgrade the Data project to use Fluent NHibernate. The issues here were pretty obvious as there were several compilation errors. I found the easiest solution was to generate a new empty Sharp Architecture project from the 1.6 template, then copy across the amended files in the NHibernateMaps folder.

This got the application to compile OK, however there were a couple of differences in the configuration from my project.

Primary Key Convention

One of the data unit tests was failing when trying to access an entity using the .Get(id) method. This was strange as the entity was being loaded OK in the setup method. I was also getting an exception from the website when trying to add an entity to the database:

Invalid object name ‘hibernate_unique_key’.

The problem was due to a change in the PrimaryKeyConvention class. The convention for generating primary keys has been changed from Native() to HiLo(“1000″). So instead of the first record having a key of 0 it would now try to use 1000. Changing this setting back to Native() fixed the problem straight away.

Cascade Conventions

The other issue I encountered occurred when trying to add a new ingredient to a recipe:

Cannot insert the value NULL into column ‘IngredientFk’, table ‘recipebook.dbo.RecipeIngredientDrafts’; column does not allow nulls. INSERT fails.
The statement has been terminated.

This error pointed to a problem with NHibernate not saving changes to associated entities; it was trying to save a new RecipeIngredientDraft record without first saving the new Ingredient record. This issue could be resolved by adding a call to instance.Cascade.SaveUpdate() in the ReferenceConvention and HasManyConvention classes.

After fixing these issues the application worked just fine :)

Optimising data access queries (Part 2)

In part 1 of this post I implemented a custom repository method to find all ingredients containing a keyword. The obvious improvement for this query was to extend the search to multiple keywords. As usual the first step was to write a unit test for the new method:

        protected override void LoadTestData()
        {
            CreatePersistedIngredient(1, "Chicken thigh", FoodGroup.Meat);
            CreatePersistedIngredient(2, "Chicken breast", FoodGroup.Meat);
            CreatePersistedIngredient(3, "Red Pepper", FoodGroup.Vegetable);
            CreatePersistedIngredient(4, "Jalapeno Chilli", FoodGroup.Vegetable);
        }

        [Test]
        public void CanFindIngredientsByKeywords()
        {
            List<Ingredient> ingredients = ingredientRepository.FindByKeywords(new string[] { "chilli", "pepper" }).ToList();
            ingredients.ShouldNotBeNull();
            ingredients.Count.ShouldEqual(2);
        }

The test expects 2 ingredients to be returned for the search – both the ‘Red pepper’ and ‘Jalapeno Chilli’ ingredients should be returned.

Predicate functions and Expression trees

I wanted to keep using the Linq-to-NHibernate library for the data access queries as this ensures that the queries inherit all the benefits of Linq. I also find it more intuitive to use than HQL, for simple queries at least! The core problem was how to write a Linq query that could be extended for an arbitrary number of keywords. In HQL this could be achieved by looping over the keywords and building up the query string. However it is not so easy to iteratively build a lambda function.

My first effort involved chaining together predicate functions. However after passing the chained predicate to NHibernate the rather less than impressive result was nothing… for any query! Looking at the SQL query produced showed that an incomplete statement was being passed without any of the filter keywords. I was unable to find any documentation to speak of for Linq-to-NHibernate but the NHibernate user group did prove useful. They suggested that I may have more success building up an expression tree and using that as the predicate.

Dynamic Linq-to-NHibernate Query

After reading up on expression trees I was in a position to code up a method to dynamically build the predicate. While researching the best way to tackle this, the internet again reminded me that there are not many original problems left in this world! This article by Joseph Albahari on dynamically composing expression predicates demonstrated how a helper class could be used to farm out the work of building up the predicate. The following class makes use of the PredicateBuilder class provided in the article:

        public IEnumerable<Ingredient> FindByKeywords(IEnumerable<string> keywords)
        {
            Expression<Func<Ingredient, bool>> predicate = PredicateBuilder.False<Ingredient>();

            foreach (string keyword in keywords)
            {
                string temp = keyword;
                predicate = predicate.Or(i => i.Name.Contains(temp));
            }
            return Session.Linq<Ingredient>().Where(predicate);
        }
  • The expression predicate is initialised to false
  • The predicate is chained together using the Or method of predicate builder class
  • The final predicate will return true for any Ingredient that has a Name containing any of the keywords.

The FindByKeywords method passes the unit test at the start of the post. Implementing this method took a while, mostly due to the time taken to read up on the predicate functions and expression trees. However I think it was time worth spending as these language features are here to stay and will most likely increase in use over time. Also I now have a handy way of writing efficient Linq queries with multiple filter parameters :)

Optimising data access queries (Part 1)

In this post I will be looking at the best way to re-factor a data access query which is used on the add ingredients page. Initially the query was written quickly with no consideration given to the time it would take to execute. It is not currently a problem as the database is very small but in future the page could be very slow to load.

    ingredientsModel.MatchingIngredients = IngredientRepository.GetAll()
        .Where(i => i.Name.ToLower().Contains(ingredientsModel.SearchIngredient.ToLower())).ToList();

This is a poorly optimised query as the generic Sharp Architecture repository GetAll() method call retrieves the entire data set from the database. It is then filtered in memory to get the actual matching ingredients. For larger datasets, it would be much quicker if the data is filtered before it is retrieved from the database. The query could be rewritten to use the FindAll(IDictionary propertyValuePairs) method like this:

    ingredientsModel.MatchingIngredients = IngredientRepository
        .FindAll(new Dictionary { { "Name", ingredientsModel.SearchIngredient.ToLower()} });

This does solve the optimisation issue as the resulting database SQL query uses a Where clause to pre-filter the data. However, the query is limited to retrieving exact matches to the search string. It is unlikely that users will input the exact ingredient they are searching for, so it would be better if the query retrieved all ingredients that contain the search term.

Unit Testing the Custom Repository

In order to do this I will use a custom repository and add my own query method. Much credit is due to the Sharp Architecture example projects, which provide a good basis for the techniques I’ve used here. First, I’ll write a unit test for the new repository method. The test class is shown below and each section is explained after the class:

    [TestFixture]
    public class IngredientRepositoryTests : RepositoryTestsBase
    {
        protected override void LoadTestData()
        {
            CreatePersistedIngredient(1, "Chicken thigh", FoodGroup.Meat);
            CreatePersistedIngredient(2, "Chicken breast", FoodGroup.Meat);
            CreatePersistedIngredient(3, "Red Pepper", FoodGroup.Vegetable);
            CreatePersistedIngredient(4, "Jalapeno Chilli", FoodGroup.Vegetable);
        }

        private IIngredientRepository ingredientRepository = new IngredientRepository();

        private Ingredient CreatePersistedIngredient(int ingredientId, string ingredientName, FoodGroup foodGroup)
        {
            Ingredient ingredient = new Ingredient(ingredientName, foodGroup);
            ingredientRepository.SaveOrUpdate(ingredient);
            FlushSessionAndEvict(ingredient);
            return ingredient;
        }

        [Test]
        public void CanFindIngredientsByName()
        {
            List<Ingredient> ingredients = ingredientRepository.FindByName("chicken").ToList();
            ingredients.ShouldNotBeNull();
            ingredients.Count.ShouldEqual(2);
        }
    }
  • The unit test inherits from the RepositoryTestsBase (namespace SharpArch.Testing.NUnit.NHibernate) which enables test data to be easily loaded into the in-memory database by overriding the the LoadTestData method.
  • The ingredientRepository property is the custom repository data access class
  • The CreatePersistedIngredient method is used in the LoadTestData method to save a single ingredient entity to the in-memory database. Note the call to FlushSessionAndEvict is used to persist the saved entity to the database
  • The CanFindIngredientsByName method is our test method which runs the repository FindByName method 
  • The test method asserts that the ingredients list is populated with 2 Ingredient entities

Implementing the Custom Repository

Now we are ready to implement the custom repository for the accessing ingredient data. Again, the class is shown below with explanations following it:

    public class IngredientRepository : Repository<Ingredient>, IIngredientRepository
    {        
        public IEnumerable<Ingredient> FindByName(string ingredientName)
        {
            return Session.Linq<Ingredient>().Where(a => a.Name.Contains(ingredientName));
        }
    }
  • The custom repository inherits from the generic sharp architecture Repository class, ensuring that all the standard access methods are available
  • It implements the IIngredientRepository interface. This enables IoC injection of the repository into the controllers.
  • The FindByName method implements a Linq to NHibernate query using the Where method. This will be transformed into a SQL query using a Where clause to prefilter the data before it is retrieved.

The custom repository method passes the unit test and returns the expected data. The original query is now optimised and will perform much better against larger data sets. I’ve improved the efficiency of the data access, however I’d also like to improve the functionality. So in the next post I will be looking at extending the ingredient repository with a new dynamic query. This will enable searching by an arbitrary number of keywords.

Add recipe feature – task breakdown

The add recipe feature is the first feature that will added to the site. This task can first be broken down into 4 subtasks:

  1. Add a recipe summary
  2. List / add ingredients
  3. Add recipe ingredients
  4. Add a recipe cooking method

However, the sharp architecture framework is designed for Test Driven Development. These are the TDD steps listed in the sharp architecture reference guide (in the docs folder of the SVN project):

  1. Write your test as if the target objects and API already exists.
  2. Compile the solution and see it break.
  3. Write just enough code to get it to compile.
  4. Run the test and see if fail.
  5. Write just enough code to get it to pass.
  6. Run the test and see it pass!
  7. Refactor if necessary!

Taking another look at the feature subtasks from a TDD perspective, the breakdown for each domain object looks more like this:

Ingredient

  1. Add tests for the ingredient domain object and controller
  2. Implement the ingredients domain object and controller to make the tests pass
  3. Add Ingredients views for List and Create


Recipe

  1. Add tests for the recipe domain object and controller
  2. Implement the recipe domain object and controller to make the tests pass
  3. Add Recipe views for CreateSummary, CreateIngredients, CreateCookingMethod


Currently I just need to implement the Create view for the Ingredient object, then I can move onto recipes.

Before we begin…

I’ve started this blog to record my progress making a website in ASP.NET MVC. I am hoping that writing a blog about the experience will help keep the development moving forwards. The motivation for making the website is to get a better understanding of the technologies that are used to build it. Therefore it is not so important that the site is unique, more that I can implement interesting and challenging features on the site to stretch the framwork. I don’t expect many people will read the blog but maybe one day it will have some interesting content!

A summary of the progress so far:

  • Run through tutorials on ASP.NET MVC, primarily the excellent NerdDinner tutorial.
  • Checked out Sharp Architecture to use as project architecture
  • Initial idea about making the site on an online recipe book, where users can add and store their own recipes

Future targets:

  • Draft some wireframes for the website
  • Draft an initial class diagram for the project
  • Build a prototype using Sharp Architecture

I think wireframes will be useful to help clarify what I want in terms of the sites appearance and functionality. So, I’ll be putting on a designers hat (if I can find one) for a brief time and bash something out. It might not be pretty at this stage but I don’t want it to delay me too much on starting the development.