11

I've recently read a lot of arguments against using the repository pattern with powerful ORM's like Entity Framework as it incorporates repository-like functionality, along with Unit of Work functionality as well.

Another argument against using the pattern for a situation like unit testing is that the repository pattern is a leaky abstraction since the more generic implementations leverage IQueryable.

The arguments against using the repository pattern make sense to me, but the alternative methods of abstractions suggested are often more confusing and appear just as overkill as the problem.

Jimmy Bogards solution seems to be a mix of blowing the abstractions away, but also introducing his own architecture. https://lostechies.com/jimmybogard/2012/10/08/favor-query-objects-over-repositories/

Another example of repositories being unnecessarily....but use my architecture! http://blog.gauffin.org/2012/10/22/griffin-decoupled-the-queries/

Another... http://www.thereformedprogrammer.net/is-the-repository-pattern-useful-with-entity-framework

I haven't found a clear replacement or alternative to the "overly complex" repository pattern approach that isn't more architected itself.

  • 4
    What specifically are you trying to achieve? Abstractions should have a purpose. If you are writing a CRUD app an ORM is probably abstract enough. – JacquesB Mar 18 '16 at 21:15
  • @JacquesB I'm trying to avoid object relational impedance issues with a robust domain model, but also abstract that away from my viewmodels in an mvc implementation. – AnotherDeveloper Mar 18 '16 at 21:59
  • Reed Cospey has a lot of positive things to say about IQuerable here: stackoverflow.com/questions/1578778/using-iqueryable-with-linq This implies it's better at the transport layer. As far as abstraction, I found use for the Generic Repository pattern work well when I needed to inject the EntityType but still wanted to maintain common methods. On the other hand, I myself have argued on MSDN LINQ forum that EF is a repository pattern because it's all in memory. One project used numerous Where clauses as method calls which worked well. – John Peters Mar 19 '16 at 0:20
  • But one area I looked into but rejected was the Expression Tree business, some really like it, but....you have to study it hard before you find it useful. – John Peters Mar 19 '16 at 0:20
  • 2
    we should go back to calling SQL from controllers – bobek Mar 25 '16 at 14:27
12

I think you are conflating repositories and generic repositories.

A basic repository just interfaces your data store and provides methods to return the data

IRepository {
   List<Data> GetDataById(string id);
}

It doesn't leak the data layer into your code via an IQueryable or other ways of passing in random queries and provides a well defined testable and injectable surface of methods.

A Generic Repository allows you to pass in your query much like an ORM

IGenericRepository<T> {
    List<T> Get<T>(IQuery query);
    //or
    IQueryable<T> Get<T>();
}

I agree there isn't much point using a Generic Repository on top of an ORM which is basically just another Generic Repository.

The answer is to use the basic Repository pattern to hide your ORM

  • 1
    An ORM of an ORM? I was trying to be funny. Why do you need to abstract the ORM? – johnny Jul 5 '17 at 18:58
  • 1
    same reason you abstract anything. to avoid polluting your code with proprietary classes – Ewan Jul 5 '17 at 19:53
6

Most of the arguments you mention wrongly attribute to the Repository pattern features that it doesn't have.

Conceptually, a Repository as originally defined in DDD is just a collection of objects that you can search or add to. The persistence mechanism behind it is abstracted out, so as a consumer you get the illusion that it's an in-memory collection.

A Repository implementation that has leaky abstractions (exposing IQueryables for instance) is a poor Repository implementation.

A Repository implementation that exposes more than just collection operations (for instance, Unit of Work features) is a poor Repository implementation.

Are there alternatives to Repository for data access ? Yes, but they are not related to the issues you bring up in your question.

1

To me, repositories, combined with ORM or other DB persistence layers, have these disadvantages:

  1. Covering up Units of Work. UoW have to be coded by the programmer and can rarely be implemented as a kind of magic in the background, where the user simply makes queries and modifications, without defining the UoW boundaries and possibly the commit point. Sometimes, the UoW are abandoned by reducing them into micro UoW (e.g. NHibernate sessions) in each Repository access method.
  2. Covering up, or, in the worst case, destroying Persistence Ignorance: Methods like "Load()", "Get()", "Save()" or "Update()" suggest immediate, single object operations, as if sending individual SQL/DML, or as if working with files. In fact, for example, the NHibernate methods, with these misleading names, usually don't make individual access, but enqueue for lazy load or insert/update batch (Persistence Ignorance). Sometimes, programmers wonder why they don't get immediate DB operations and forcibly break up persistence ignorance, thus killing performance and using major efforts to actually make the system (much!) worse.
  3. Uncontrolled Growth. A simple repository might accumulate more and more methods to fit specific needs.

Such as:

public interface ICarsRepository  /* initial */
{
    ICar CreateNewCar();
    ICar LoadCar(int id); // bad, should be for multiple IDs.
    void SaveCar(ICar carToSave); // bad, no individual saves, use UoW commit!
}

public interface ICarsRepository  /* a few years later */
{
    ICar CreateNewCar();
    ICar LoadCar(int id); 
    IList<ICar> GetBlueCars();
    IList<ICar> GetRedYellowGreenCars();
    IList<ICar> GetCarsByColor(Color colorOfCars); // a bit better
    IList<ICar> GetCarsByColor(IEnumerable<Color> colorsOfCars); // better!
    IList<ICar> GetCarsWithPowerBetween(int hpFrom, int hpTo);
    IList<ICar> GetCarsWithPowerKwBetween(int kwFrom, int kwTo);
    IList<ICar> GetCarsBuiltBetween(int yearFrom, int yearTo);
    IList<ICar> GetCarsBuiltBetween(DateTime from, DateTime to); // some also need month and day
    IList<ICar> GetHybridCarsBuiltBetween(DateTime from, DateTime to); 
    IList<ICar> GetElectricCarsBuiltBetween(DateTime from, DateTime to); 
    IList<ICar> GetCarsFromManufacturer(IManufacturer carManufacturer); 
    bool HasCarMeanwhileBeenChangedBySomebodyElseInDb(ICar car); // persistence ignorance broken
    void SaveCar(ICar carToSave);
}

4. Danger of God object: you might be tempted to create one god class, covering all of your model or data access layer. The repository class would not only contain Car methods, but methods for all entities.

In my opinion, it is better to offer at least some query opportunities, to avoid the huge mess of many single purpose methods. No matter if it is LINQ, an own query language, or even something taken directly from the ORM (OK, kind of coupling problem...).

1

If the purpose of the Repository-interface is to mock away the database for a unittest (= test in isolation) the best abstraction is something that is easy to mock.

It is difficuilt to mock a repository interface that is based on a IQueryable result.

From a Unit-testing point of view

IRepository {
   List<Data> GetDataById(string id);
}

can be mocked easily

IGenericRepository<T> {
    List<T> Get<T>(IQuery query);
}

can be mocked easy only if the mock ignores the content of the query parameter.

IGenericRepository<T> {
    IQueryable<T> Get<T>(some_parameters);
}

cannot be easily mocked

0

I don't think repository pattern is overkill, if you use lambda functions for querying. Especially when you have to abstract the ORM (in my opinion you always should) then I won't care the implementation details of the repository itself.

For example:

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        UserRepository ur = new UserRepository();
        var userWithA = ur.GetBy(u => u.Name.StartsWith("A"));

        Console.WriteLine(userWithA.Name);


        ur.GetAllBy(u => u.Name.StartsWith("M"))
          .ForEach(u => Console.WriteLine(u.Name));


        ur.GetAllBy(u => u.Age > 13)
          .ForEach(u => Console.WriteLine(u.Name));
    }
}

public class UserRepository 
{
    List<User> users = new List<User> { 
        new User{Name="Joe", Age=10},
            new User{Name="Allen", Age=12},
            new User{Name="Martin", Age=14},
            new User{Name="Mary", Age=15},
            new User{Name="Ashton", Age=29}
    };

    public User GetBy(Predicate<User> userPredicate)
    {
        return users.Find(userPredicate);
    }

    public List<User> GetAllBy(Predicate<User> userPredicate)
    {
        return users.FindAll(userPredicate);
    }
}

public class User
{
    public string Name { get; set; }

    public int Age { get; set; }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.