Repository pattern and convention-based Entity Framework code first
This post continues our investigations into the more useful parts of Microsoft's premier ORM, Entity Framework. Previous installments include automatic migrations (including screencast) and loading and querying spatial data using Entity Framework. Today, we'll improve the sample code from the migrations post to be completely convention-based and we will also create a repository implementation to access data.
Building the model
You might remember from the automatic migrations post that we built a setup for simple convention-based migrations that are executed seamlessly as the model is expanded and updated. There is one smell in that sample though: We want database schema to be created for all classes that inherit from an abstract Entity
class, but what actually defines what goes in the database are the DbSet property declarations in the Context
class:
public class Context : DbContext
{
public DbSet<User> Users { get; set; }
}
That's bad: To add an entity to our data model we have to both create the class and add a property to the DbContext
-derived class. Luckily, Rowan Miller from the Entity Framework Team has an example demonstrating how to dynamically create a code first model. Adapting our OnModelCreating()
implementation to use that approach results in this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
ConfigureModel(modelBuilder);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, Configuration>());
base.OnModelCreating(modelBuilder);
}
private void ConfigureModel(DbModelBuilder modelBuilder)
{
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
var entityTypes = Assembly.GetAssembly(typeof(Entity)).GetTypes()
.Where(x => x.IsSubclassOf(typeof(Entity)) && !x.IsAbstract);
foreach (var type in entityTypes)
{
entityMethod.MakeGenericMethod(type).Invoke(modelBuilder, new object[] { });
}
}
The code above works by identifying all non-abstract classes derived from Entity
and invoking the DbModelBuilder.Entity()
method for each of those classes. With this, we no longer need the DbSet
property in the Context
class.
Creating a repository
Without any DbSet
properties on the Context
, we can still access entities using the DbContext.Set method. For this tutorial, we'll do something else though: Abstract away Entity Framework by wrapping use of the Context
in a repository. Using the repository will insulate the rest of your code from any changes you might want to make in you ORM implementation and will avoid tying up the rest of your project up on Entity Frameworks particulars. In a future blog post, we'll use this flexibility to demonstrate completely switching out the underlying ORM.
The repository conforms to this interface:
public interface IRepository
{
T Get<T>(int id) where T : Entity;
IQueryable<T> GetAll<T>() where T : Entity;
void SaveOrUpdate<T>(T entity) where T : Entity;
void Delete<T>(T entity) where T : Entity;
}
The full implementation is online. With that, we can simplify our HomeController
to use the new repository:
public class HomeController : Controller
{
private readonly IRepository _repository = new Repository(new Context());
public ActionResult Index()
{
return View(_repository.GetAll<User>());
}
public ActionResult Create(User user)
{
_repository.SaveOrUpdate(user);
return RedirectToAction("Index");
}
}
And even better, we've reduced the complexity of adding new classes to the data model: You only have to add a class derived from Entity
and then the reflection-based model configuration and Entity Framework migrations will automatically create the relevant schema changes. The full code sample is available online.