Implementation of a Generic Repository Beauty, with Ninject the Beast

I really should be studying. But why study when I can blog right? Well, I have been meaning to write this post for a while now, and here it is.

The Repository Pattern is invaluable when working with the data, particularly in MVC. It encapsulates a lot of redundant and hopefully generic operations, I use it to encapsulate operations on the Entity Framework Code First ORM. My implementation doesn’t use Unit of Work which tracks changes and updates the database accordingly. Instead, I let myself decide. It’s not that bad.

The Context

Firstly, there is the context interface. It can expose methods, but I didn’t need to. Let’s imagine this context is for a shop.
This interface is important because it’ll be used by Ninject to wire everything up.

public interface IShopContext
{
}

The concrete implementation of the context might look like this.

public class CareContext: DbContext, IShopContext
{
    public DbSet<Fruit> Fruits { get; set; }

    public ShopContext()
    {
    }
}

The Entities

Fruit above is an entity and there can be many more. The entities derive from an abstract Identity class. What it is, is pretty obvious.

public abstract class Identity
{
    public Guid ID { get; set; }
}

Now let’s look at the example Fruit.

public class Fruit: Identity
{
    public string Name { get; set; }
    public int AmountInShop { get; set; }
}

The Repository

public interface IRepository<TEntity> where TEntity : Identity
{
    TEntity Add(TEntity model, bool persist = false);
    TEntity Get(Guid id);
    IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> predicate);
    IEnumerable<TEntity> GetAll();

    TEntity Update(TEntity model, bool persist = false);

    void Remove(Guid id, bool persist = false);
    void Remove(TEntity model, bool persist = false);

    void Save();
}

So these are some of the operations that the repository will perform. As you can see, I take care of the persistence using the optional parameter persist.

And now… the concrete implementation of the repository.

public class Repository<TEntity> : IRepository<TEntity> where TEntity : Identity
{
    private DbContext Context;
    private DbSet<TEntity> DBSet;

    public Repository(IShopContext context)
    {
        Context = context as DbContext;
        DBSet = Context.Set<TEntity>();
    }

    public TEntity Get(Guid id)
    {
        TEntity model = DBSet.Find(id);
        return model;
    }

    public IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> predicate)
    {
        return DBSet.Where(predicate).AsEnumerable();
    }

    public IEnumerable<TEntity> GetAll()
    {
        return DBSet.AsEnumerable<TEntity>();
    }

    public TEntity Add(TEntity model, bool persist = false)
    {
        model.ID = Guid.NewGuid();
        DBSet.Add(model);
        Save(persist);

        return model;
    }

    public TEntity Update(TEntity model, bool persist = false)
    {
        Context.Entry(model).State = EntityState.Modified;
        Save(persist);

        return model;
    }

    public void Remove(Guid id, bool persist = false)
    {
        TEntity model = DBSet.Find(id);
        Remove(model, persist);
    }

    public void Remove(TEntity model, bool persist = false)
    {
        if (model != null)
        {
            Context.Entry<TEntity>(model).State = EntityState.Deleted;
            Save(persist);
        }
    }

    public void Save()
    {
        Save(true);
    }

    private void Save(bool persist)
    {
        if (persist)
        {
            Context.SaveChanges();
        }
    }
}

There you have it. It’s very simple to read really. It just spells out the methods defined in the interface and also provides logic for persistence when you want it.

The Ninject

Now all of this is pretty useless without Ninject. You’ll have noticed I used Inversion of Control while implementing the repository. Ninject is available on Nuget.

While registering the services on Ninject, I can tell it to provide a ShopContext when a IShopContext is required. The repository is bound to an interface, this context can be a development or deployment context, depending. Also, since I want to use the same context always, I bind it using ToConstant.

As for when a repository operating on a certain entity is requested, Ninject finds its type and provides accordingly.

private static void RegisterServices(IKernel kernel)
{
    IShopContext shopContext = new ShopContext();
    kernel.Bind(typeof(IShopContext)).ToConstant(shopContext);
    kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
}

Hope that wasn’t too long a post, it probably is my longest so far. Till next time, then.

About these ads

5 Comments on “Implementation of a Generic Repository Beauty, with Ninject the Beast”

  1. Kris says:

    With a constant instance of ShopContext, don’t you think that you might run into a situation where there are two requests executing at the same time, both with pending changes, where one finishes before the other and calls Save(true) before the other request is done (which means that the longer request would have some of it’s changes be part of one transaction and the remainder of it’s changes in another transaction)?

    • Kowsheek says:

      Yes definitely, data access layers always have concurrency problems and can be resolved by some of the suggestions here. I am looking to improve my implementation, possibly with an optimistic concurrency check.

  2. babe says:

    this is interesting , thank you.

  3. aregaz says:

    It’s not clear to me: where should I call RegisterServices method in MVC application?
    One time for each controller? Or just ones? But where?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 256 other followers