Ninject and Entity Framework Code First: Easy repository patterns
by Phil on May 25th, 2011
When I was still learning the basics of Dependency Injection (DI), Repositories and Object Relational Mappers (ORM), it was a pretty long struggle to get my head around when much of my professional career had revolved around web forms and .Net 2.
It was only through playing around with code in my spare time that things gradually fell into place. I’m not a gifted developer savant like Scott Hanselman, Steve Sanderson or Rob Conery. Stuff doesn’t just “make sense” as soon as I read it. I need to walk through slowly and see stuff working first before trying to understand how it works.
So having gone through many failed or flawed implementations of the repository pattern and almost-but-not-quite getting DI to work correctly (let alone trying to get my head around Inversion of Control), I believe I’ve finally hit upon a clean, simple repository structure that allows good separation of concerns, highly-testable code and makes coding up my MVC3 sites an absolute doddle. It should be noted that I’ve only been able to reach this point by reading, observing examples and borrowing heavily from other cases out there in the world.
Over the next few posts, I’m going to discuss what I’ve found is working great for me, what’s actually happening and why I’ve done it that way. I’ll also go down a few side-routes to discuss other useful tools or tricks that I’ve found increase my productivity or standard of work.
I’m going to use the same example project throughout: an ASP.Net MVC3 application for support ticket management. This project uses the new Razor syntax, a bit of jQuery and some other fun odds & ends.
Getting Started
The first thing I’m obviously going to want to do is create my MVC3 web application. I’ve started up Visual Studio 2010 and create a new ASP.Net MVC 3 Web Application. I’ll select the Internet Application template using Razor as the View Engine. I don’t have any NUnit test projects installed yet, so I’ll leave the “Create a unit test project” box unchecked and manually add this later.
I’m going to want to do some housekeeping later to organise my classes into logical locations, adding new projects to hold them. For starters though, I’ll need to create projects to manage my data transactions and to define my application models.
I created two new projects &ndash Data and Models – to fulfill these tasks, and also created a Tests project for my unit tests (obviously).
I’m pretty keen to get some actual database work kicked off at this point, but first I need to define my entity models. Let’s have a look at what I’ll need to create a basic, workable support ticketing system:
Ticket – this object will contain the core information about incidents and change requests
Assignment – this object will keep track of who the current owner of a given ticket is
Customer – this object contains the details for companies and contacts within those companies
Solution – this object contains information about how a ticket was resolved and its root cause
There are a few supplementary classes that I’ll need to support each of these classes, such as call categories, priorities, states, etc. Check the source code example that I’ve included at the end of this post to see the full data model.
In order to keep everything in its logical place, I’m going to move the automatically-generated AccountModels from the actual web application project to my new Models project. That means I need to add a couple of references to the project:
System.Web
System.Web.ApplicationServices
System.Configuration
I can then delete my entire MVC web app Models folder. It might feel a little weird not seeing your Models in the same project but don’t panic – our namespaces are still all the same, we’re just separating things out to be a bit more organised. Our web app will never even know the difference.
My fundamental data entity is the Ticket. Here’s the code for it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.DataAnnotations;
namespace Opendesk.Models
{
public partial class Ticket
{
public int TicketId { get; set; }
public string ReferenceId { get; set; }
public DateTime Logged { get; set; }
public string LoggedBy { get; set; }
[MaxLength]
public string Description { get; set; }
public int ContactId { get; set; }
public virtual Category Category { get; set; }
public virtual Priority Priority { get; set; }
public virtual Status Status { get; set; }
public virtual Source Source { get; set; }
public virtual CallType Type { get; set; }
public virtual Assignment Assignment { get; set; }
public virtual ICollection Journals { get; set; }
public virtual ICollection Attachments { get; set; }
}
public partial class Ticket
{
public Ticket()
{
this.Logged = DateTime.Today;
}
}
}
As I mention above, have a look at the source code at the end of this post to see the full set of models.
Creating the Repository
The repository object is where all the excitement happens. This project is the key component for our Entity Framework data handling and where we define the behaviours for our code-first approach to data modelling and database management.
There are three key parts to this project:
Our base repository
The database mappings
The data context (connection) handling
The base repository is only responsible for the basic CRUD operations. I want to define an interface that sets out a contract for Add, Delete, Update methods as well as methods for getting a single instance and all instances. Here’s my code:
using System.Collections.Generic;
namespace Opendesk.Data.Interfaces
{
/// <summary>
/// Defines the methods that are required for a Repository
/// </summary>
/// <typeparam name="T">Any type of class available in the <see cref="OpendeskDb.Models"/> namespace.</see>
public interface IRepository<T> where T : class
{
void Add(T Entity);
void Update(T Entity);
void Delete(T Entity);
T GetById(int Id);
IEnumerable<T> All();
}
}
While I’m on the subject of interfaces, I’ll create one that defines the methods for getting the actual entity framework Db Context. I’m going to call my DbContext instance “OpendeskDb”. Here’s the code:
using System.Data.Entity;
namespace Opendesk.Data.Interfaces
{
///
/// Defines the methods that are required for a DatabaseFactory instance.
///
public interface IDatabaseFactory
{
///
/// Returns a concrete instance of the OpendeskDb data context
///
/// DbContext (OpendeskDb)
OpendeskDb Get();
}
}
I need a reference to the Entity Framework 4 assembly here (and will do in other projects as well), so now seems like a good time to grab it. I use Nuget to download / update my external code dependencies like ELMaH, Entity Framework and the like. If you aren’t using Nuget, may I suggest you consider saving yourself some time by reading Scott Hanselman’s NuGet Action Plan. You can get more information on the latest (at time of writing) release of the Entity Framework on Scott Gu’s blog.
So far, so good. Next, I need to set up mappings for my data models. This is where things get a little more detailed. This class is responsible for querying and working the actual entity data in object form. It returns all entities as type DbSet<T> collections or instances. Here’s the (lengthy) code:
using System.Data.Entity;
using Opendesk.Models;
using System.Collections.Generic;
namespace Opendesk.Data
{
///
/// Provides facilities for querying and working with entity data as objects.
///
///
/// Note that a section of code has been commented out in this class that allows
/// dynamic dropping and recreation of the data model (i.e. the database) but does
/// not currently seed the newly-created database with new data.
///
public class OpendeskDb : DbContext
{
private IDbSet tickets;
///
/// Returns a DbSet of s, this allows CRUD
/// operations to be performed for the given entity in the context.
///
public IDbSet Tickets
{
get { return tickets ?? (tickets = DbSet()); }
}
public IDbSet Assignments { get; set; }
public IDbSet Solutions { get; set; }
public IDbSet Journals { get; set; }
public IDbSet Customers { get; set; }
public IDbSet Contacts { get; set; }
public IDbSet CallTypes { get; set; }
public IDbSet Sources { get; set; }
public IDbSet Attachments { get; set; }
public IDbSet Priorities { get; set; }
public IDbSet Statuses { get; set; }
public IDbSet Categories { get; set; }
///
/// Returns a DbSet for the specified type, this allows CRUD operations to be performed for
/// the given entity in the context.
///
public virtual IDbSet DbSet() where T : class
{
return Set();
}
///
/// Constructs a new context instance using the given string as the name or connection string
/// for the database to which a connection will be made. For more information on how this is
/// used to create a connection, see the remarks section for .
///
///
public OpendeskDb(string connectionString)
: base(connectionString)
{
tickets = Tickets;
}
///
/// Saves all changes made in this context to the underlying database.
///
public virtual void Commit()
{
base.SaveChanges();
}
//protected override void OnModelCreating(DbModelBuilder modelBuilder)
//{
// base.OnModelCreating(modelBuilder);
// Database.SetInitializer(new Initialiser());
//}
}
That’s quite a bit to take in, but I’ve also added XML comments to try and describe what’s going on. Notice also that the OnModelCreating method has been commented out at this stage. We’ll add this back in a later post.
Ok, so our core framework is sorted out, but we still don’t have any actual classes that implement the interfaces we’ve created. First, we need the DatabaseFactory class:
using System;
using System.Configuration;
using Opendesk.Data.Interfaces;
namespace Opendesk.Data
{
///
/// The DatabaseFactory class is the proxy manager for all connections and transactions with the database.
///
///
/// This class is responsible for the actual connection to the database. It holds the reference to the actual
/// database connection string from Web.Config.
///
public class DatabaseFactory : Disposable, IDatabaseFactory
{
private OpendeskDb db;
///
/// Returns the active database object context instance or creates a new instance if one doesn't exist
/// already.
///
/// A OpendeskDb object (which inherits from DbContext).
public OpendeskDb Get()
{
return db ?? (db = new OpendeskDb(ConfigurationManager.ConnectionStrings["OpendeskDb"].ConnectionString));
}
protected override void DisposeCore()
{
if (db != null)
db.Dispose();
}
}
///
/// The Disposable class is a managed disposable resource that can be explicitly called within other classes.
///
public class Disposable : IDisposable
{
private bool isDisposed;
~Disposable()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!isDisposed && disposing)
{
DisposeCore();
}
isDisposed = true;
}
protected virtual void DisposeCore()
{
}
}
}
Next we need to create the base repository class that our other classes will inherit from:
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using Opendesk.Data.Interfaces;
namespace Opendesk.Data
{
///
/// Encapsulates the available database interactions on behalf of a Repository type.
///
/// Any type of class available in the Opendesk.Models namespace.
///
///
/// The RepositoryBase type exposes the following members.
///
/// RepositoryBase
/// Add
/// Delete
/// GetById
/// All
/// Update
///
///
public abstract class RepositoryBase : IRepository where T : class
{
private OpendeskDb db;
private readonly IDbSet dbset;
///
/// Holds a reference to the DatabaseFactory class used to manage connections to the database.
///
protected IDatabaseFactory DatabaseFactory { get; private set; }
///
/// Contains a reference to the instance used by the repository.
///
protected OpendeskDb OpendeskDb { get { return db ?? (db = DatabaseFactory.Get()); } }
///
/// Initialises a new instance of the RepositoryBase class.
///
///
A valid DatabaseFactory object.
public RepositoryBase(IDatabaseFactory DbFactory)
{
DatabaseFactory = DbFactory;
dbset = OpendeskDb.Set();
}
///
/// Adds a new entity instance to the database on behalf of the parent type.
///
///
Any valid database object
public virtual void Add(T Entity)
{
dbset.Add(Entity);
db.Commit();
}
///
/// Deletes an existing instance of an entity from the database on behalf of the parent type.
///
///
Any valid database object
public virtual void Delete(T Entity)
{
dbset.Remove(Entity);
db.Commit();
}
///
/// Returns a specific instance of an entity from the database on behalf of the parent type.
///
///
The integer value of the entity's primary key
/// A database object (of type T)
public virtual T GetById(int Id)
{
return dbset.Find(Id);
}
///
/// Returns an IEnumerable collection of all objects found in the database of type T
///
/// A collection of type IEnumerable
public virtual IEnumerable All()
{
return dbset.ToList();
}
///
/// Updates an existing entity instance in the database on behalf of the parent type.
///
///
Any valid database object
public void Update(T Entity)
{
dbset.Attach(Entity);
db.Entry(Entity).State = System.Data.EntityState.Modified;
db.Commit();
}
}
}
That’s quite a lot of code, so let’s take stock of what we’ve got so far:
We defined interfaces and classes that describe our Entity Framework database management (IDatabaseFactory, DatabaseFactory and OpendeskDb); and
We defined interfaces that describe our core database interactions (IRepository and RepositoryBase)
The last main item to tick of our Data project list is the actual Ticket Repository. This class is responsible for retrieving specific subsets of data over and above the basic “get all”, “update”, “delete” and “save” functions. Again, we’ll want to define an interface so that we can unit test our concrete class and set up our Dependency Injection:
using System.Collections.Generic;
using Opendesk.Models;
namespace Opendesk.Data.Interfaces
{
///
/// Defines the methods that are required for a TicketRepository instance.
///
public interface ITicketRepository : IRepository
{
IEnumerable SearchDescription(string Description);
IEnumerable FilterByStatus(Status Status);
IEnumerable FilterBySource(Source Source);
IEnumerable FilterByCategory(Category Category);
IEnumerable FilterByType(CallType Type);
IEnumerable GetAttachmentsForTicket(int TicketId);
IEnumerable GetTicketSources();
IEnumerable GetTicketCategories();
IEnumerable GetTicketTypes();
IEnumerable GetTicketStatuses();
IEnumerable GetTicketPriorities();
}
}
We’ve defined quite a few methods here that will return ticket data according to various criteria as well as defining some methods that will primarily be needed to populate drop-down lists on our views. So let’s draw up our actual class:
using System.Collections.Generic;
using System.Linq;
using Opendesk.Data.Interfaces;
using Opendesk.Models;
namespace Opendesk.Data
{
public class TicketRepository : RepositoryBase, ITicketRepository
{
public TicketRepository(IDatabaseFactory DbFactory)
: base(DbFactory)
{ }
public IEnumerable SearchDescription(string Description)
{
return OpendeskDb.Tickets.Where(t => t.Description.Contains(Description));
}
public IEnumerable FilterByStatus(Status Status)
{
return OpendeskDb.Tickets.Where(t => t.Status == Status);
}
public IEnumerable FilterBySource(Source Source)
{
return OpendeskDb.Tickets.Where(t => t.Source == Source);
}
public IEnumerable FilterByCategory(Category Category)
{
return OpendeskDb.Tickets.Where(t => t.Category == Category);
}
public IEnumerable FilterByType(CallType Type)
{
return OpendeskDb.Tickets.Where(t => t.Type == Type);
}
public IEnumerable GetAttachmentsForTicket(int TicketId)
{
return base.GetById(TicketId).Attachments.ToList();
}
public IEnumerable GetTicketSources()
{
return OpendeskDb.Sources;
}
public IEnumerable GetTicketCategories()
{
return OpendeskDb.Categories;
}
public IEnumerable GetTicketTypes()
{
return OpendeskDb.CallTypes;
}
public IEnumerable GetTicketStatuses()
{
return OpendeskDb.Statuses;
}
public IEnumerable GetTicketPriorities()
{
return OpendeskDb.Priorities;
}
}
}
So that wraps up our data access work. All that remains to wire everything up is to take advantage of Ninject to manage our Dependency Injection and Inversion of Control.
Using Ninject for Dependency Injection
We can now return to our web project and set up our DI wiring within our Global.asax file.
When you start a garden-variety MVC application, the global.asax file looks (more or less) like this:
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
Notice that the class MvcApplication inherits System.Web.HttpApplication. Ninject has its own HttpApplication that is controlled by a native type, Ninject.IKernel.
We’ll use Nuget again to pull down Ninject and modify our Global.asax to take leverage its capabilities.
Using Nuget to add Ninject
Next, we’ll modify our MvcApplication class to inherit from NinjectHttpApplication:
using System;
using System.Web.Mvc;
using System.Web.Routing;
using Ninject;
using Ninject.Modules;
using Ninject.Web.Mvc;
using Opendesk.Data;
using Opendesk.Data.Interfaces;
namespace Opendesk
{
public class MvcApplication : NinjectHttpApplication
{
public MvcApplication()
{
}
///
/// Appends any global filters to all Controller classes in the current MVC application context.
///
///
The GlobalFilterCollection defined in the application GlobalFilters object.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Ticket",
"{controller}/{id}/{action}",
new { controller = "Ticket", action = "Index", id = UrlParameter.Optional }
);
}
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
ModelMetadataProviders.Current = new DataAnnotationsModelMetadataProvider();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
//ViewEngines.Engines.Clear();
//ViewEngines.Engines.Add(new MobileCapableWebFormViewEngine());
GlobalFilters.Filters.Add(new HandleErrorAttribute());
}
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new ServiceModule()
};
return new StandardKernel(modules);
}
}
internal class ServiceModule : NinjectModule
{
public override void Load()
{
Bind().To();
Bind().To();
Bind().To();
}
}
}
There are a couple of things to note here. First is that instead of the default method, Application_Start(), we now override OnApplicationStarted(). We also override Ninject’s CreateKernel factory method and define a new NinjectModule that dictates what our dependency bindings should be (you can see these in the internal class, ServiceModule).
That’s pretty straight-forward. Let’s see how we change our code to uncouple our controller from any strongly-typed data management dependencies.
Whereas previously we might have defined a concrete instance of a DbContext or data repository, that would have meant we were committed to having a direct line with our database that is always on. This severely limits our options for unit testing but also (perhaps more crucially) means our controller classes (or other classes) end up doing far more work than they should. So rather than marry up our controller classes to concrete dependencies, our controller constructor looks like this:
public class TicketsController : Controller
{
private readonly ITicketRepository repository;
public TicketsController(ITicketRepository Repository)
{
repository = Repository;
}
All this is doing is telling our TicketsController class that it needs to use a class that inherits an ITicketRepository. Ninject will take care of that for us.
In Summary
So what have we accomplished in all of this? Well, we’ve got a good, flexible, very loosely-coupled repository that uses Entity Framework Code First to supply us with our database, entity models and data handling. We’ve used Nuget to download and install our required third-party components; specifically Entity Framework and Ninject. We’ve then applied Ninject to our web application to create a highly-testable, flexible application.
In my next post, I’ll look at creating unit tests, creating some views using the MVC3 Razor syntax and I’ll also look at using Doxygen for on-the-go application documentation.
Thanks for the heads up. I was using the Google code formatter but I don’t think it came along for the ride when I changed my theme. I’ll sort the whole affair out very soon.
Nice blog post Phil. Looking forward to the next post around unit tests and Doxygen! I am at the beginning of the journey myself with the use of DI and IOC and this has really helped me get my head around some key concepts. Especially like the use of the generic repository!
I do have 1 question around design of ‘OpendeskDb’ but not sure this is the right forum to ask. Email me if you can and I will ask there.
My second post is on more of a slow simmer than on the boil due to a recent change in job and being VERY busy in the evenings. I will do my best to get something posted as soon as possible!
In the meantime, you’re always welcome to email me directly or catch me on Twitter: @Phil_Wheeler.
I think you’ll need to replace the < sign in your sample code with < because the generics code isn’t showing anywhere. Bit hard to read like this.
Thanks for the heads up. I was using the Google code formatter but I don’t think it came along for the ride when I changed my theme. I’ll sort the whole affair out very soon.
Nice blog post Phil. Looking forward to the next post around unit tests and Doxygen! I am at the beginning of the journey myself with the use of DI and IOC and this has really helped me get my head around some key concepts. Especially like the use of the generic repository!
I do have 1 question around design of ‘OpendeskDb’ but not sure this is the right forum to ask. Email me if you can and I will ask there.
Cheers,
Matt
Thanks for the kinds words!
My second post is on more of a slow simmer than on the boil due to a recent change in job and being VERY busy in the evenings. I will do my best to get something posted as soon as possible!
In the meantime, you’re always welcome to email me directly or catch me on Twitter: @Phil_Wheeler.
My work on Generic repository, you might wanna love or hate it but its really different and efficient than others. http://www.asifashraf.com/2011/08/my-style-of-generic-repository-with.html