MVC3 + MongoDB + Autofac

I recently posted a brief summary of creating a recipe database.  I decided to expand on that and go into the implementation details of using ASP.NET MVC3 with MongoDB and Autofac.  This code depends on the NoRM driver for MongoDB, Autofac, and MVC3 (and it’s dependencies). This is by no means the only (or best) way to do things, but this is how I chose to do it.  I’ve simplified the repository to one method for brevity, but you’d obviously want to add all of the required CRUD operations.

First, we’ll want to create an interface for our database session.  The implementation of this interface can be swapped out depending on the backing store.

public interface ISession : IDisposable
{
    System.Linq.IQueryable<T> All<T>() where T : class, new();
}

Add a MongoDB implementation of ISession.  This implementation will eventually be used in our repository.

public class MongoSession : ISession
{
    private readonly IMongo _provider;

    public MongoSession()
    {
        //this looks for a connection string in your Web.config
        //<connectionStrings>
        //  <add name="db" connectionString="mongodb://localhost/testdb?strict=true"/>
        //</connectionStrings>
        _provider = Mongo.Create("db");
    }

    public IQueryable<T> All<T>() where T : class, new()
    {
        return _provider.GetCollection<T>().AsQueryable();
    }

    public void Dispose()
    {
        _provider.Dispose();
    }
}

Create an interface for the repository.  This is what the controller will expect to be injected via it’s constructor.

public interface IRecipeRepository
{
    IEnumerable<RecipeCard> GetAll(int page = 1, int pageSize = 10);
}

Add the IRecipeRepository implementation, which is created with a new()-able type of ISession.  Why didn’t I just inject an ISession into the repository?  It’s because Mongo sessions are expected to be disposed of after use, as connections are pooled and reused.  Therefore we have to create and dispose of our sessions when using them.  This might require some workarounds if you really did switch repositories (do nothing on Dispose(), etc).

public class RecipeRepository<SessionT> : IRecipeRepository where SessionT : ISession, new()
{
    public IEnumerable<RecipeCard> GetAll(int page = 1, int pageSize = 10)
    {
        using (var session = new SessionT())
        {
            return session.All<RecipeCard>().OrderByDescending(r => r.UpdatedDate).Skip(pageSize * (page - 1)).Take(pageSize).ToList();
        }
    }
}

Now it’s time set up our IoC container.  We’ll do this in the Global.asax.cs.  Here we are registering a recipe repository of type MongoSession such that any controller that expects an IRecipeRepository in the constructor will get this.  We’re letting Autofac handle controller creation and dependency injection.  See the Autofac MVC3 integration page for more details.

protected void Application_Start()
{
    var builder = new ContainerBuilder();
    builder.Register(r => new RecipeRepository<MongoSession>()).As<IRecipeRepository>().InstancePerLifetimeScope();
    builder.RegisterControllers(Assembly.GetExecutingAssembly());

    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
}

Finally, here is our controller.  Notice we never have to create a repository object – one was injected for us.

private readonly IRecipeRepository recipeRepository;

public RecipesController(IRecipeRepository recipeRepository)
{
    this.recipeRepository = recipeRepository;
}

public ViewResult Index()
{
    return View(recipeRepository.GetAll());
}

That’s it.