Alternatives to the repository pattern for encapsulating ORM logic?

  softwareengineering

I’ve just had to switch out an ORM and it was a relatively daunting task, because the query logic was leaking everywhere. If i’d ever had to develop a new application, my personal preference would be to encapsulate all query logic (using an ORM) to futureproof it for change. Repository pattern is quite troublesome to code and maintain so i was wondering if there are any other patterns to solve the problem ?

I can foresee posts about not adding extra complexity before it’s actually needed, being agile etc. but i’m only interested about the existing patterns solving a similar problem in a simpler way.

My first thought was to have a generic type repository, to which i add methods as needed to specific type repository classes via extension methods, but unit testing static methods is awfully painful. IE:

public static class PersonExtensions
{
    public static IEnumerable<Person> GetRetiredPeople(this IRepository<Person> personRep)
    {
        // logic
    }
}

2

First of all: A generic repository should be considered to be a base class and not complete implementations. It should help you to get the common CRUD methods in place. But you still have to implement the query methods:

public class UserRepository : EntityFrameworkRepository<User>, IUserRepository
{
    public UserRepository(DbContext context){}

    public ICollection<Person> FindRetired()
    {
        return context.Persons.Where(p => p.Status == "Retired").ToList();
    }
}

Second:

Do not return IQueryable. It’s a leaky abstraction. Try to implement that interface yourself or use it without knowledge about the underlying db provider. For instance, every db provider has it’s own API for eagerly load entities. That’s why it’s a leaky abstraction.

Alternative

As an alternative you can use queries (for instance as defined by the Command/Query separation pattern). CQS is described in wikipedia.

You basically create query classes which you invoke:

public class GetMyMessages
{
    public GetMyMessages(IDbConnection connection)
    {
    }


    public Message[] Execute(DateTime minDate)
    {
        //query
    }
}

the great thing with queries is that you can use different data access methods for different queries without cluttering the code. you can for instance use a web service in one, nhibernate in another and ADO.NET in a third.

If you are interested in a .NET implementation read my article: http://blog.gauffin.org/2012/10/griffin-decoupled-the-queries/

6

First think I say is that ORM is already big enough abstraction over your database. Some ORMs provide binding to all common relational databases (NHibernate has bindings to MSSQL, Oracle, MySQL, Postgress, etc..) So building new abstraction on top of it doesn’t seem profitable to me. Also, argummenting, that you need to “abstract away” this ORM is meaningless.

If you still want to build this abstraction, I would go against Repository pattern. It was great in age of pure SQL, but it is quite troublesome in face of modern ORM. Mainly because you end up re-implementing most of the ORM features, like CRUD operations and querying.

If I was to build such abstractions, I would use these rules/patterns

  • CQRS
  • Use as much as possible of existing ORM features
  • Encapsulate only complex logic and queries
  • Try to have “plumbing” of the ORM hidden inside an architecture

In concrete implementation, I would use CRUD operations of ORM directly, without any wrapping. I would also do simple queries directly in code. But complex queries would be encapsulated in their own objects. To “hide” the ORM, I would try to inject the data context transparently into an service/UI objects and have same done to query objects.

Last thing I would like to say, is that many people use ORM without knowing how to use it and how to make best “profit” out of it. People recommending repositories are usually of this kind.

As a recommended reading, I would say Ayende’s blog, especially this article.

2

The problem with the leaky implementation is that you need many different filter conditions.

You can narrow this api if you implement a repository method FindByExample and use it like this

// find all retired persons
Person filter = new Person {Status=PersonStatus.Retired};
IEnumerable<Person> found = personRepository.FindByExample(filter);


// find all persons who have a dog named "sam"
Person filter = new Person();
filter.AddPet(new Dog{Name="sam"});
IEnumerable<Person> found = personRepository.FindByExample(filter);

Consider making your extensions operate on IQueryable instead of IRepository.

public static class PersonExtensions
{
    public static IQueryable<Person> AreRetired(this IQueryable<Person> people)
    {
        return people.Where(p => p.Status == "Retired");
    }
}

To unit test:

List<Person> people = new List<Person>();
people.Add(new Person() { Name = "Bob", Status = "Retired" });
people.Add(new Person() { Name = "Sam", Status = "Working" });

var retiredPeople = people.AsQueryable().AreRetired();
// assert that Bob is in the list
// assert that Sam is not in the list

No fancing mocking required for testing.
You can also combine these extension methods to create more complex queries as needed.

1

I wrote a pretty neat query object pattern for NHibernate here: https://github.com/shaynevanasperen/NHibernate.Sessions.Operations

It works by using an interface like this:

public interface IDatabases
{
    ISessionManager SessionManager { get; }

    T Query<T>(IDatabaseQuery<T> query);
    T Query<T>(ICachedDatabaseQuery<T> query);

    void Command(IDatabaseCommand command);
    T Command<T>(IDatabaseCommand<T> command);
}

Given a POCO entity class like this:

class Database1Poco
{
    public int Property1 { get; set; }
    public string Property2 { get; set; }
}

You can build query objects like this:

class Database1PocoByProperty1 : DatabaseQuery<Database1Poco>
{
    public override Database1Poco Execute(ISessionManager sessionManager)
    {
        return sessionManager.Session.Query<Database1Poco>().SingleOrDefault(x => x.Property1 == Property1);
    }

    public int Property1 { get; set; }
}

And then use them like this:

var database1Poco = _databases.Query(new Database1PocoByProperty1 { Property1 = 1 });

3

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT