Code First with Generic Repositories and BDD (“that’s exciting” eh, Rob Conery?)

Intro

--

I’m writing this blog entry to review my initial use of Entity Framework 4 Code First in combination with generic repositories (via a repository factory) and my personal BDD (behaviour driven development) methodology. Apart from helping me to consolidate my learning, allowing me an opportunity to refactor and generally improve my work (this example is based on the re-design of my website), I’m also hoping for comments from others on anything covered in this blog post which can help me to improve as a developer. So please comment away.



Since my birth as a Web Developer into this world of 0’s and 1’s, I’ve worked as a part of a team who are enthusiastic about not just software technologies — but good practices, clean code and optimal methodologies. As part of the latter, one of the latest trends seems to be BDD — Behaviour Driven Development (please see the useful links section at the bottom for BDD resources to get you going) which is one of the focal points of this blog post.



Assuming you now, or already did, know what BDD is your personal opinion may be that it applies only to User Acceptance Testing (which was the impression I got from this book)with UI testing such as Watin. Or you may counter that and say BDD is a total replacement for TDD — including Unit Tests (which is the impression I got from Rob Conery whilst watching his series on Tekpub where he builds a blog using the Ruby on Rails framework [links at bottom of page if you’re interested]).



For me, I prefer to use BDD instead of TDD — as a total replacement. And so in this blog post, I’ll write my unit tests as specs with SpecFlow (links to SpecFlow at bottom of the page). If you have an opinion on this, then please get in touch and share your thoughts and rationales. There appear to be no clear-cut rules to BDD and learning from others is the best way for me to hone my own approach.

Setting the Scene

My goal for today is to replace my Entity Framework DataContext and the generated manure it creates. I want to do this in the context of my own personal web site which I am redesigning using ASP.NET MVC in a BDD fashion. The only reason I have these in the first place was so I could have a play with EF4 in the context of a real application. On commercial projects, at the judicious decision of my boss, I use the brilliant Castle Active Record. Seeing as Code First is Microsoft’s attempt at implementing the Active Record design pattern, this will also be a great chance for me to compare the technologies (and give my boss a nudge if needs be).



The current set up of my web site in terms of ORM is based on the repository pattern. I also have a repository factory that instantiates all repositories. This repository factory takes an IDataContext (a custom interface) that I used to wrap the EF4 Data Context . Therefore, all I need to do to get Code First implemented, is to implement a new version of IDataContext, that wraps Code First, and switch that with the EF4 version.



This blog post will only cover that my Code First data context works in integration and I will not be testing the repositories. Nonetheless, I felt it was important to disclose the full requirements of it.

For a visual representation of what I’ve tried to explain, have a look at this:

public class RepositoryFactory : IRepositoryFactory

{

private IDataContext DataContext { get; set; }

public RepositoryFactory(IDataContext dataContext)

{

DataContext = dataContext;

}

public IRepository GetRepository() where T : class

{

var repository = new Repository(DataContext);

return repository;

}

}

This is my repository factory; You tell it what kind or repository you want and it will make you one. Two things to note:

1. There is an implementer of IDataContext passed to the repository — this provides the underlying data store and is the focal point of this blog post.

2. Notice how the IDataContext is passed into the factory’s constructor. Using dependency injection, I will never have to new one of these up and can do a site-wide switch for another implement or of IDataContext (maybe I’ll decide I don’t like code first and go back standard EF4).

For DI (or IoC) I use Ninject. Below is the snippet of DI code in global.asax pertinent to this blog post. Here is also the only place I would need to change the IDataContext used if I decided to use a different ORM tool.

internal class ServiceModule : NinjectModule

{

public override void Load()

{

Bind<IRepositoryFactory>().To<RepositoryFactory>().InSingletonScope();

Bind<IDataContext>().To<NTCodingDataContext>().InSingletonScope();

}

}

Below is the interface I’ll need to adhere to (as mentioned above) when creating the class that wraps the interface of Code First.

public interface IDataContext

{

void
Add(T newItem) where T : class;

IEnumerable
Where(Expression<Func<T, bool>>
expression) where T : class;

IEnumerable
FindAll() where T : class;

void
Delete(T itemToDelete) where T : class;

void
SaveChanges();

}

Writing my Specs

With the scenario laid out, the specs can be written. So using the IDataContext interface as a basis, here are the specs I came up with.

Feature: Managing Data

In order to store domain information entities for the web application

As a developer

I need a data access strategy to manage them

Scenario: Addition With Save Changes

Given a new Book

When I add the Book to the DataContext

And I Save Changes

Then the Book should exist in the DataContext

Scenario: Addition Without Save Changes

Given a new Book

When I add the Book to the DataContext

And I dont Save Changes

Then the Book should not exist in the DataContext

Scenario: Deletion With Save Changes

Given a new Book

When I add the Book to the DataContext

And I Save Changes

And I Delete the Book

Then the Book should exist in the DataContext

Scenario: Deletion Without Save Changes

Given a new Book

When I add the Book to the DataContext

And I Save Changes

And I Delete the Book

And I Save Changes

Then the Book should not exist in the DataContext

Scenario: Querying by Type

Given the data store contains collections for different types of object

When I ask for all objects of a certain type

Then only objects of that type should be returned

Even though the specs still talk about a ‘DataContext’, in general, the language is still behaviour-oriented. I feel this benefit of having readable tests, where you don’t rely on a very_long_method_name_in_this_fashion, is still beneficial even at this unit/integration test level.

Implementing the Specs

This could turn out quite long if I cover each step of every spec. So, I’ll cover the first spec which includes most of the setup work and then selected snippets from each of the other steps and their implementation, if worthwhile.



First issue I need to resolve is adding items to be persisted and ensuring I confirm this request by calling SaveChanges on the DataContext. To achieve this, though, I’ll need a step file to implement my scenario.

With that step file added, my specs project takes the following form:

As you can see , I’ve not created a separate project for my integration specs (tests), rather a folder in the specs project. On a small-scale app like this I have no issues against myself doing so.

So, in BDD style, I write out the spec implementations before creating any code. But by making my tests fail first, SpecFlow will show me the steps I need to implement… And here they are with my implementation:

[Binding]

public class DataContextSpecs

{

private NTCodingDataContext _dataContext;

private Book NewBook { get; set; }

public DataContextSpecs()

{

_dataContext = new NTCodingDataContext();

if (_dataContext.Database.Exists())

{

_dataContext.Database.Delete();

}

_dataContext.Database.Create();

}

[Given(@”a new Book”)]

public void GivenANewBook()

{

NewBook = new Book

{

DateAdded = DateTime.Now,

Description = “SupaDupa”,

ImageUrl = “NoUrl”,

Title = “SupaDupa”,

};

}

[When(@”I add the Book to the DataContext”)]

public void WhenIAddTheBookToTheDataContext()

{

_dataContext.AddItem(NewBook);

}

[When(@”I Save Changes”)]

public void WhenISaveChanges()

{

_dataContext.SaveChanges();

}

[Then(@”the Book should exist in the DataContext”)]

public void ThenTheBookShouldExistInTheDataContext()

{

Assert.IsTrue(_dataContext.GetCollection<Book>().Any(b => b.Title == NewBook.Title));

}

}

Just to caveat a bad habit, I do not recommend deleting and recreating the database every time you run specs. Following the principle of YAGNI, it is acceptable at this moment in time (Code First has better options for this type of scenario anyway see either of the Code First links at the bottom of this page).

All I’ve had to do in these steps is call the methods on the NTCodingDataContext that are provided by the IDataContext interface. Not a lot to it really; it is the next step that is the crux of this blog post.

Creating a DataContext for Code First

As shown in the steps above, the new DataContext will be called NTCodingDataContext. With the wonders of ReSharper, generating the class with required methods was easy. Here’s what that class looked like after being implemented:

public class NTCodingDataContext
: DbContext, IDataContext

{

public DbSet<Book>
Books { get; set;
}

public DbSet<Author>
Authors { get; set;
}

public DbSet<Genre>
Genres { get; set;
}

public DbSet<HomePage>
HomePage { get; set;
}

public IQueryable GetCollection() where T : class

{

return
ObjectContext.CreateObjectSet().AsQueryable();

}

public void AddItem(T item) where T : class

{

var
items = ObjectContext.CreateObjectSet();

items.AddObject(item);

}

public void DeleteItem(T item) where T : class

{

var
items = ObjectContext.CreateObjectSet();

items.DeleteObject(item);

}

public void Add(T newItem) where
T : class

{

var
items = ObjectContext.CreateObjectSet();

items.AddObject(newItem);

}

public IEnumerable Where(Expression<Func<T,
bool>> expression) where T : class

{

return
ObjectContext.CreateObjectSet().AsQueryable().Where(expression);

}

public IEnumerable FindAll() where T : class

{

return
ObjectContext.CreateObjectSet();

}

public void Delete(T itemToDelete) where T : class

{

var
items = ObjectContext.CreateObjectSet();

items.DeleteObject(itemToDelete);

}

public new void
SaveChanges()

{

base.SaveChanges();

}

}

I’m sorry, but me and YAGNI had a bit of a fall out. I’ve gone and implemented methods that I didn’t even need yet. Well actually, I did. To meet the criteria of the interface I had to implement them at this time else the project wouldn’t compile and I’d fall out with the compiler instead (she trumps YAGNI).



Anyway, running down the class, let’s see what Code First essentials can be uncovered.

Starting right at the top you can see the class inherits DbContext. From my education so far, this is an essential step and in essence replaces the data context usually seen in EF4 and LINQ to SQL.



Next are the four DbSet’s. Without these, the DataContext will not know which objects it needs to create tables and do mappings for — thus classes without a corresponding DbSet will just not be included (unless mapped using another option. See links at the bottom of page). This contrasts with Castle Active Record where you just need to put an attribute on top of your class and it becomes part of your ORM system.



Having only had a brief play with Code First, I think there is a fluent interface and possibly attributes you can use instead of putting a DbSet in this class. But to be fair to Code First, you don’t need to add any Code First-specific attributes to your domain classes — the default option is to go by convention and I congratulate them for that (your classes can stay nice and clean and have no idea what ORM they are being used with).



When using Castle Active Record, your classes require the [ActiveRecord] attributes, and suddenly become coupled to that framework. With Code First, there appear to be options, defaults even, to save you having to do anything with your domain classes.



Moving further down the class, most of the methods make a call to the ObjectContext. As opposed to specifying which collection (books, authors, genres etc) you can call the CreateObject set and pass in the type — perfectly facilitating my needs for generic repositories. Of course, this isn’t new; this is very similar to standard EF4 and is also nothing new to Castle Active Record geeks.

Completing the Steps

With the DataContext implemented, the rest of the scenarios in my feature file could be completed quite easily by adding the appropriate steps. Doing so was a simple case of just calling methods on the DataContext and using dummy data as a means of verification.



Below is my completed step file with an implementation for the steps/scenarios I haven’t talked about.

[Binding]

public class DataContextSpecs

{

private NTCodingDataContext _dataContext = new NTCodingDataContext();

private IList<Book> DefaultBookList;

private IList<Author> authors;

private IList<Genre> genres;

private Book NewBook { get; set; }

private DbSet<Book> ReturnedBooks { get; set; }

public DataContextSpecs()

{

_dataContext = new NTCodingDataContext();

if (_dataContext.Database.Exists())

{

_dataContext.Database.Delete();

}

_dataContext.Database.Create();

InitialiseCollections();

}

private void InitialiseCollections()

{

authors = new List<Author>();

genres = new List<Genre>();

DefaultBookList = new List<Book>();

for (int i = 0; i < 3; i++)

{

authors.Add(new Author

{

Name = “TestAuthor” + i

});

genres.Add(new Genre

{

Name = “Test Genre” + i

});

DefaultBookList.Add(new Book

{

DateAdded = DateTime.Now,

Description = “Test Book” + i,

Title = “Test Book” + i,

ImageUrl = “no image”

});

}

}

[Given(@”a new Book”)]

public void GivenANewBook()

{

NewBook = new Book

{

DateAdded = DateTime.Now,

Description = “SupaDupa”,

ImageUrl = “NoUrl”,

Title = “SupaDupa”,

};

}

[Given(@”the data store contains collections for different types of object”)]

public void GivenTheDataStoreContainsCollectionsForDifferentTypesOfObject()

{

DefaultBookList.ToList().ForEach(_dataContext.AddItem);

authors.ToList().ForEach(_dataContext.AddItem);

genres.ToList().ForEach(_dataContext.AddItem);

_dataContext.SaveChanges();

}

[When(@”I ask for all objects of a certain type”)]

public void WhenIAskForAllObjectsOfACertainType()

{

ReturnedBooks = _dataContext.Set<Book>();

}

[When(@”I add the Book to the DataContext”)]

public void WhenIAddTheBookToTheDataContext()

{

_dataContext.AddItem(NewBook);

}

[When(@”I Delete the Book”)]

public void WhenIDeleteTheBook()

{

var book = _dataContext.GetCollection<Book>().Where(b => b.Title == NewBook.Title).First();

if (book != null)

{

_dataContext.DeleteItem(book);

}

}

[When(@”I Save Changes”)]

public void WhenISaveChanges()

{

_dataContext.SaveChanges();

}

[When(@”I dont Save Changes”)]

public void WhenIDontSaveChanges()

{

// nothing to see here

}

[Then(@”only objects of that type should be returned”)]

public void ThenOnlyObjectsOfThatTypeShouldBeReturned()

{

Assert.IsTrue(ReturnedBooks.Count() == DefaultBookList.Count);

}

[Then(@”the Book should exist in the DataContext”)]

public void ThenTheBookShouldExistInTheDataContext()

{

Assert.IsTrue(_dataContext.GetCollection<Book>().Any(b => b.Title == NewBook.Title));

}

[Then(@”the Book should not exist in the DataContext”)]

public void ThenTheBookShouldNotExistInTheDataContext()

{

Assert.IsFalse(_dataContext.GetCollection<Book>().Any(b => b.Title == NewBook.Title));

}

}

The Database

At this point I haven’t mentioned the database being used. Well, all I did was take the default approach (again, no code needed!) and by doing nothing, literally, Code First created a database for me. I chose this path for ease. So if this is all you’ve read about Code First, then please go and see the Scott Gu or Scott Hanselman links at the bottom of this page and you will see there are a number of options for choosing how to link a database with Code First.

Did it Work Then?

According to SpecFlow, we are on course for Mars. Here’s the readout after running these specs in the test runner.

— — — Test started: Assembly: Specs.dll — — —

Given a new Book

-> done: DataContextSpecs.GivenANewBook() (6.2s)

When I add the Book to the DataContext

-> done: DataContextSpecs.WhenIAddTheBookToTheDataContext() (0.0s)

And I dont Save Changes

-> done: DataContextSpecs.WhenIDontSaveChanges() (0.0s)

Then the Book should not exist in the DataContext

-> done: DataContextSpecs.ThenTheBookShouldNotExistInTheDataContext() (0.7s)

Given a new Book

-> done: DataContextSpecs.GivenANewBook() (0.9s)

When I add the Book to the DataContext

-> done: DataContextSpecs.WhenIAddTheBookToTheDataContext() (0.0s)

And I Save Changes

-> done: DataContextSpecs.WhenISaveChanges() (0.1s)

Then the Book should exist in the DataContext

-> done: DataContextSpecs.ThenTheBookShouldExistInTheDataContext() (0.0s)

Given a new Book

-> done: DataContextSpecs.GivenANewBook() (1.1s)

When I add the Book to the DataContext

-> done: DataContextSpecs.WhenIAddTheBookToTheDataContext() (0.0s)

And I Save Changes

-> done: DataContextSpecs.WhenISaveChanges() (0.0s)

And I Delete the Book

-> done: DataContextSpecs.WhenIDeleteTheBook() (0.4s)

And I Save Changes

-> done: DataContextSpecs.WhenISaveChanges() (0.0s)

Then the Book should not exist in the DataContext

-> done: DataContextSpecs.ThenTheBookShouldNotExistInTheDataContext() (0.0s)

Given a new Book

-> done: DataContextSpecs.GivenANewBook() (1.0s)

When I add the Book to the DataContext

-> done: DataContextSpecs.WhenIAddTheBookToTheDataContext() (0.0s)

And I Save Changes

-> done: DataContextSpecs.WhenISaveChanges() (0.0s)

And I Delete the Book

-> done: DataContextSpecs.WhenIDeleteTheBook() (0.0s)

Then the Book should exist in the DataContext

-> done: DataContextSpecs.ThenTheBookShouldExistInTheDataContext() (0.0s)

Given the data store contains collections for different types of object

-> done: DataContextSpecs.GivenTheDataStoreContainsCollectionsForDifferentTypesOfObject() (1.2s)

When I ask for all objects of a certain type

-> done: DataContextSpecs.WhenIAskForAllObjectsOfACertainType() (0.0s)

Then only objects of that type should be returned

-> done: DataContextSpecs.ThenOnlyObjectsOfThatTypeShouldBeReturned() (0.2s)

5 passed, 0 failed, 0 skipped, took 15.41 seconds (NUnit 2.5.5).

Conclusion

As someone with a similar experience level (personal projects and goofing around) of normal Entity Framework 4 I just don’t see what Entity Framework 4 without Code First does better. Let’s start with some things about EF 4 I found unhelpful.



Interactions with the Database

With EF4 you have to do your magic in the designer, generate the scripts and then completely rewrite your database every time you add new domain objects….Give me a break.

For people working in an agile team, who want to add bits as they need it, I see this as a big hindrance. Using Code First instead, it is not a problem. It creates and updates your database automatically and can be configured to update or recreate in a way that suits your project’s needs



Generated Classes

I really did not enjoy having to use those designer generated classes. This was especially painful because I was already using Castle Active Record on commercial projects which had none of this mess. Well, like Active Record, Code First doesn’t do it either and I really appreciate that.

Comparing with Castle Active Record

First up, Castle Active Record is enjoyable to work with and having the option to use it I would never touch standard EF4 on a commercial project (if you’re reading, boss, I’d do anything you say. One sugar or two?). But that’s not including Code First, which on first looks is possibly the superior implementation of the Active Record pattern for .NET coders. It is early days yet and I’ve only skimmed a few blog posts and experimented with the code shown in this blog post, but so far so good and a tentative applause to Microsoft.



One advantage to Microsoft over Castle are the defaults in Code First — they mean you don’t need to adorn your classes or methods with any attributes which tie them to your ORM. Castle Active Record does need these types of declarations. But it is worth pointing out my use of Code First is on a very small project and I have no complex relationships or business rules to manage on my domain objects. I expect this could influence things in a big way. And let’s not forget, using .NET 4 you get buddy classes which could be used to house the attributes and still keep your domain classes clean of ORM “stuff”.

Final Note

I have enjoyed swapping “designer” EF4 for Code First and think Code First has so far worked well for my small application. Over the coming months I hope to learn more about Code First and hope to pay more congratulations to Microsoft for their implementation of the Active Record pattern.

As mentioned, if you have any comments on anything covered in this post from my use of the technologies to my BDD-style methodology, then pick up the bat phone (leave me a comment).

Useful Links

Code First

Scott Gu’s Initial Code First Introduction

Hanselman’s Code First Demo



BDD (Behaviour Driven Develoment)


Dan North — Introducing BDD

http://blog.stevensanderson.com/2010/03/03/behavior-driven-development-bdd-with-specflow-and-aspnet-mvc/

SpecFlow

Rob Conery’s Tekpub Series — Building My Own Blog (Rails)

--

--

Nick Tune
Strategy, Architecture, Continuous Delivery, and DDD

Principal Consultant @ Empathy Software and author of Architecture Modernization (Manning)