With the new release of the GenericRepository.EntityFramework package, we now have clean, better and sexier generic repository implementation for Entity Framework. Enjoy!
@ 01-10-2013
by Tugberk Ugurlu


I have written a few blog posts about my experience on applying the Generic Repository pattern to Entity Framework and I even made a NuGet package for my naïve implementation. Even if that looked OK at the time for me, I had troubles about my implementation for couple of reasons:

  • The implementations inside the NuGet package didn’t allow the developer to share the DbContext instance between repositories (per-request for example).
  • When the generic repository methods weren’t enough, I was creating new repository interfaces and classes based on the generic ones. This was the biggest failure for me and didn’t scale very well as you can imagine.
  • There were no pagination support.
  • As each repository take a direct dependency on DbContext and it is impossible to entirely fake the DbContext, generic repositories needed to be mocked so that we could test with them. However, it would be just very useful to pass a fake DbContext instance into the repository implementation itself and use it as fake.

With the new release of the GenericRepository.EntityFramework package, the all of the above problems have their solutions. The source code for this release is available under the master branch of the repository and you can also see the ongoing work for the final release under the v0.3.0 branch. The NuGet package is available as pre-release for now. So, you need to use the –pre switch to install it.

PM> Install-Package GenericRepository.EntityFramework -Pre

The old GenericRepository.EF package is still around and I will update it, too but it’s now unlisted and only thing it does is to install the GenericRepository.EntityFramework package.

I also included a sample application which shows the usage briefly. I will complete the sample and extend it further for a better view. Definitely check this out!

Let’s dive right in and see what is new and cool.

IEntity and IEntity<TId> Interfaces

I introduced two new interfaces: IEntity and IEntity<TId> and each of your entity classes needs to implement one of these. As you can see from the implementation, IEntity just implements the IEntity<int> and you can use IEntity if you are using integer based Ids. The reason why I added these is make the GetSingle method work.

Use EntitiesContext Instead of DbContext

Instead of deriving your context class from DbContext, you now need to take the EntitiesContext as the base class for your context. If you have an existing context class based on DbContext, changing it to use EntitiesContext should not break it. The EntitiesContext class has all the same constructors as DbContext. So, you can also use those. Here is the sample:

public class AccommodationEntities : EntitiesContext {

    // NOTE: You have the same constructors as the DbContext here. E.g:
    // public AccommodationEntities() : base("nameOrConnectionString") { }

    public IDbSet<Country> Countries { get; set; }
    public IDbSet<Resort> Resorts { get; set; }
    public IDbSet<Hotel> Hotels { get; set; }
}

Then, through your IoC container, you can register your context as a new instance for IEntitiesContext per a particular scope. The below example uses Autofac to do that for an ASP.NET Web API application:

private static void RegisterDependencies(HttpConfiguration config) {

    var builder = new ContainerBuilder();
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

    // Register IEntitiesContext
    builder.Register(_ => new AccommodationEntities())
           .As<IEntitiesContext>().InstancePerApiRequest();

    // TODO: Register repositories here

    config.DependencyResolver = 
        new AutofacWebApiDependencyResolver(builder.Build());
}

IEntityRepository<TEntity> and EntityRepository<TEntity>

Here is the real meat of the package: IEntityRepository and EntityRepository. Same as the IEntity and IEntity<TId>, we have two different IEntityRepository generic interfaces: IEntityRepository<TEntity> and IEntityRepository<TEntity, TId>. They have their implementations under the same generic signature: EntityRepository<TEntity> and EntityRepository<TEntity, TId>. The big improvement now is that EntityRepository generic repository implementation accepts an IEntitiesContext implementation through its constructor. This, for example, enables you to use the same DbContext (IEntitiesContext implementation in our case, which is EntitiesContext by default) instance per-request for your ASP.NET MVC, ASP.NET Web API application and share that across your repositories. Note: don’t ever use singleton DbContext instance throughout your AppDomain. DbContext is not thread safe.

As we have registered our EntitiesContext instance per request above, we can now register the repositories as well. As our repositories accepts an IEntitiesContext implementation through their constructor, our IoC container will use our previous registration for that automatically. Autofac has this ability as nearly all IoC containers do.

private static void RegisterDependencies(HttpConfiguration config) {

    var builder = new ContainerBuilder();
    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

    // Register IEntitiesContext
    builder.Register(_ => new AccommodationEntities())
           .As<IEntitiesContext>().InstancePerApiRequest();

    // TODO: Register repositories here
    builder.RegisterType<EntityRepository<Country>>()
           .As<IEntityRepository<Country>>().InstancePerApiRequest();
    builder.RegisterType<EntityRepository<Resort>>()
           .As<IEntityRepository<Resort>>().InstancePerApiRequest();
    builder.RegisterType<EntityRepository<Hotel>>()
           .As<IEntityRepository<Hotel>>().InstancePerApiRequest();

    config.DependencyResolver = 
        new AutofacWebApiDependencyResolver(builder.Build());
}

Out of the Box Pagination Support

Best feature with this release is out of the box pagination support with generic repository instances. It doesn’t perform the pagination in-memory; it queries the database accordingly and gets only the parts which are needed which is the whole point Smile Here is an ASP.NET Web API controller which uses the pagination support comes with the EntityRepository:

public class CountriesController : ApiController {

    private readonly IEntityRepository<Country> _countryRepository;
    private readonly IMappingEngine _mapper;
    public CountriesController(
        IEntityRepository<Country> countryRepository, 
        IMappingEngine mapper) {

        _countryRepository = countryRepository;
        _mapper = mapper;
    }

    // GET api/countries?pageindex=1&pagesize=5
    public PaginatedDto<CountryDto> GetCountries(int pageIndex, int pageSize) {

        PaginatedList<Country> countries = 
             _countryRepository.Paginate(pageIndex, pageSize);

        PaginatedDto<CountryDto> countryPaginatedDto = 
           _mapper.Map<PaginatedList<Country>, PaginatedDto<CountryDto>>(countries);

        return countryPaginatedDto;
    }
}

public interface IPaginatedDto<out TDto> where TDto : IDto {

    int PageIndex { get; set; }
    int PageSize { get; set; }
    int TotalCount { get; set; }
    int TotalPageCount { get; set; }

    bool HasNextPage { get; set; }
    bool HasPreviousPage { get; set; }

    IEnumerable<TDto> Items { get; }
}

public class PaginatedDto<TDto> : IPaginatedDto<TDto> where TDto : IDto {

    public int PageIndex { get; set; }
    public int PageSize { get; set; }
    public int TotalCount { get; set; }
    public int TotalPageCount { get; set; }

    public bool HasNextPage { get; set; }
    public bool HasPreviousPage { get; set; }

    public IEnumerable<TDto> Items { get; set; }
}

Paginate method will return us the PaginatedList<TEntity> object back and we can project that into our own Dto object as you can see above. I used AutoMapper for that. If I send a request to this API endpoint and ask for response in JSON format, I get back the below result:

{
    "PageIndex":1,
    "PageSize":2,
    "TotalCount":6,
    "TotalPageCount":3,
    "HasNextPage":true,
    "HasPreviousPage":false,
    "Items":[
      {
        "Id":1,
        "Name":"Turkey",
        "ISOCode":"TR",
        "CreatedOn":"2013-01-08T21:12:26.5854461+02:00"
      },
      {
        "Id":2,
        "Name":"United Kingdom",
        "ISOCode":"UK",
        "CreatedOn":"2013-01-08T21:12:26.5864465+02:00"
      }
    ]
}

Isn’t this perfect Smile There are other pagination method inside the EntityRepository implementation which supports including child or parent entities and sorting. You also have the ToPaginatedList extension method and you can build your query and call ToPaginatedList on that query to get PaginatedList<TEntity> object back.

Extending the IEntityRepository<TEntity>

In my previous blog posts, I kind of sucked at extending the generic repository. So, I wanted to show here the better approach that I have been taking for a while now. This is not a feature of my generic repository, this is the feature of .NET itself: extension methods! If you need extra methods for your specific repository, you can always extend the IEntityRepository<TEntity, TId> which gives you a better way to extend your repositories. Here is an example:

public static class HotelRepositoryExtensions {

    public static IQueryable<Hotel> GetAllByResortId(
        this IEntityRepository<Hotel, int> hotelRepository, int resortId) {

        return hotelRepository.FindBy(x => x.ResortId == resortId);
    }
}

What is Next?

My first intention is finish writing all the tests for the whole project, fix bugs and inconsistencies for the v0.3.0 release. After that release, I will work on EF6 version for my generic repository implementation which will have sweet asynchronous support. I also plan to release a generic repository implementation for MongoDB.

Stay tuned, install the package, play with it and give feedback Winking smile




Hi 🙋🏻‍♂️ I'm Tugberk Ugurlu.
Coder 👨🏻‍💻, Speaker 🗣, Author 📚, Microsoft MVP 🕸, Blogger 💻, Software Engineering at Deliveroo 🍕🍜🌯, F1 fan 🏎🚀, Loves travelling 🛫🛬
Lives in Cambridge, UK 🏡