Code Aesthetics

Programming, agility, technical stuff ...

Entity Framework 4.0 Code-First vs NHibernate

Microsoft's .NET Framework Version 4 includes a version of the Entity Framework, which allows two ways of creating your model: Database First and Model First. Database First was already available for the previous version of Entity Framework. It means that Visual Studio's Wizard is used to point to a database and Entity Framework generates the model classes and mappings out of the database tables. Very often the domain model looks different than the data model, which means either developing another mapping layer between the generated classes of Entity Framework or a lot of customization on the generated classes themselves. For this reason it is also possible to use the Model-First approach, which allows you to use the graphical designer and design your model first and then let entity framework generate the database schema and mappings.  Lately another approach has been introduced as a CTP: Code-First.  Instead of using the designer,  Code-First allows you to code your domain with POCOs and persist this plain model using Entity Framework. This article compares Entity Framework 4 Code-First CTP with NHibernate in terms of usability (not performance-wise) using examples of domain driven design concepts.

The sample code is a small application for managing business contacts and companies with addresses and relations. The domain model looks something like the following:

Contact Manager Domain Model

To encapsulate the data access and decouple it from the domain classes, the repository and unit of work pattern has been implemented:

Data Access

Entity Framework Implementation

The unit of work is a simplified version because Entity Framework 4 has already its own implementation of a unit of work, which is the DbContext class. The configuration of the DbContext class can be done like this:

public class DatabaseContext : DbContext  
{
     protected override void OnModelCreating(ModelBuilder builder)
 {
 builder.Configurations.Add(new AddressDetailsConfiguration());
 builder.Configurations.Add(new AddressConfiguration());
 builder.Configurations.Add(new CompanyConfiguration());
 builder.Configurations.Add(new ContactConfiguration());
 builder.Configurations.Add(new RelationConfiguration());
 }

 public DbSet<Company> Companies { get; set; }
 public DbSet<Contact> Contacts { get; set; }
 public DbSet<Relation> Relations { get; set; }
 public DbSet<Address> Addresses { get; set; }
}

It is also possible to define the keys and relations directly in this class (See the next listing).

public class DatabaseContext : DbContext  
{
 protected override void OnModelCreating(ModelBuilder builder)
 {
 builder.Entity<Company>().HasKey(c => c.Id);
 ...
 }

 public DbSet<Company> Companies { get; set; }
 public DbSet<Contact> Contacts { get; set; }
 public DbSet<Relation> Relations { get; set; }
 public DbSet<Address> Addresses { get; set; }
}

In the current implementation the configuration has been moved to separate classes.  The fluent interface configuration allows us to leave the actual domain classes untouched. Entity Framwork 4 Code-First picks up keys and relations automatically if the naming scheme adheres to a convention (e.g. id of the Company class should be named id or CompanyId). It is also possible to use data annotations in order to tell entity framework which property relates to which unique key etc. In my opinion the fluent interface is the nicest approach because the domain classes stay untouched. The following code listing shows such a configuration class for Company:

class CompanyConfiguration : EntityConfiguration<Company>{  
 public CompanyConfiguration() {
 this.HasKey(c => c.Id);
 this.Property(c => c.Id).IsIdentity();
 this.HasMany<Relation>(c => c.Relations);
 this.HasMany<Address>(c => c.Addresses);
 }
}

The big advantage of having the a mapping configuration in code instead of having it in a configuration file is that it can be unit tested. To do integration tests of the database layer, it is possible to define strategies for database initialization. You do not need to clean up the database after each test run, you can start with a new one:

[TestClass]
public class CompanyRepositoryFixture  
{
 UnityContainer _container;
 IRepository<Company> _repository;
 UnitOfWork _unitOfWork;

 [ClassInitialize]
 public static void ClassInitialize(TestContext c) {
 Database.SetInitializer(new AlwaysRecreateDatabase<DatabaseContext>());
 Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
 }

 [TestInitialize]
 public void Initialize()
 {
 _container = new UnityContainer();
 _container.RegisterInstance<IRepositoryFactory>(new RepositoryFactory());
 UnitOfWork uow = new UnitOfWork();
 _container.RegisterInstance<IUnitOfWork>(uow);
 _container.RegisterType<RepositoryBase<Company>, CompanyRepository>();
 _container.RegisterType<RepositoryBase<Contact>, ContactRepository>();
 ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(_container));
 _unitOfWork = uow;
 _repository = Repositories.Get<Company>();
 }

 [TestCleanup]
 public void Cleanup() {
 _unitOfWork.Context.Database.Initialize();
 }

 private Company AddCompany()
 {
 var company = new Company("Microsoft");
 _repository.Add(company);
 _unitOfWork.Commit();
 Assert.IsTrue(company.Id > 0);
 return company;
 }

 [TestMethod]
 public void TestAddCompany()
 {
 Assert.AreEqual(0, _repository.FindAll().Count());
 var company = AddCompany();
 Assert.AreEqual(1, _repository.FindAll().Count());
 }

 // ... all other tests left out!
}

Line 10 defines the strategy:

Database.SetInitializer(new AlwaysRecreateDatabase<DatabaseContext>());  

This of course is only interesting if you want to define some integration tests that actually hit the database. On another level of abstraction like testing the domain model, the repositories should be mocked in order to completely be independent of the database. Mocking framework such as TypeMock or Moq can be really useful for that purpose.

Comparison with NHibernate

In the .NET world there are a lot of different Object Relational Mappers (ORM).  One open-source ORM, which has already been used on many projects is NHibernate  (a port of java's hibernate).  There are a lot of additional libraries which can be used. The NHibernate implementation can be really similar to the Entity Framework implementation if we use Fluent NHibernate which offers a way to define the mappings between domain model and tables  in code classes (see the following code listing).

public class CompanyMap : ClassMap<Company>  
{
 public CompanyMap() {
 Id(c => c.Id);
 Map(c => c.Name);
 HasMany<Relation>(c => c.Relations).Cascade.All();
 }
}

As you can see it looks really similar to Microsoft's fluent interface. It is also possible to decorate the domain model classes with annotations which indicate relations and keys. One difference to the entity framework is that it is needed to make all methods and properties virtual in order to let NHibernate create proxy classes to do its lazy loading magic (if you want to use Entity Framework's lazy loading features, you also need to do that - but it is not mandatory if you don't want to use it).  Apart from that you have support for a lot of different databases, much more than Entity Framework provides. Another missing feature on the current CTP of Entity Framework is the lack of cascading deletions, which NHibernate supports. Maybe this will be supported in the final release of Code-First.

Conclusion

Entity Framework Code-First fills the gap of flexibility, which is missing in Database-First and Model-First. Let's see if the final release can offer as much as NHibernate (in combination with Fluent NHibernate) can offer today.

Downloads

Here is the sample source code I used to play around with EF 4 Code-First. It also contains some sample code for a View-ViewModel implementation in WPF using Unity, MEF (Managed Extensibility Framework) and WAF (WPF Application Framework).

ContactManager Source

Johannes Täuber

I am a software architect and agility advocate. I am interested in all technologies out there and like to discuss options. My platform of choice is the .NET framework.