Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphism with generics on the repository pattern

I have a problem using the Repository pattern in C# especially when I try to implement a Façade pattern too. My concept follows:

When I first started the generic repository, I began with one which has all the CRUD operations in a single file (and their related interfaces in a separate single file). But because of the SOLID principles and in particular the ISP principle, I decided to segregate all the interfaces into separate files, and do likewise with the classes.

So for example instead of having IGenericRepo with the various Create, Read ... methods in one file, and the corresponding GenericRepo in another. I segregated them all out having a base repo to do anything common. So I ended up with an ICreateRepo, an IReadOneRepo, an IReadManyRepo...etc.

As time passed, my project needs grew, and I found myself needing multiple "Read" operations:

  • Read a single record with the given id,
     public T Read(int id)
  • Read a single record which could potentially have compound primaries having to pass in multiple values. Works like the EF Find Method...i.e.
     public T Read(params object[] keyValues)
  • Return the first record based on a search on any field...like the EF Where Method, which had parameter like...
     public T Read(Expression<Func<T>,bool>> predicate)

This was fine until I came across a situation where I needed to read multiple records and return a list of all records that matched the criteria. Essentially the read operation had the same method signature as the last one mentioned, differing only in its return type. One returned a single entity, the other returned a list of matching entities.

   public IQueryable<T> Read(Expression<Func<T>,bool>> predicate)

While separated out into their own classes there was no issue. However I found in my controllers (I use MVC) that I had long lists of repositories about 6 or 9 and I wanted to simplify this to one like what I had for the single generic. So I turned to the Façade pattern.

Now when I bring together the read functionalities, I will have a problem with polymorphic behaviour because the signatures are the same.

    //ReadOne 1
    public T Read(int id)
    { }
    //ReadOne 2
    public T Read(params object[] keyValues)
    { }
    //ReadOne 3 *** Signature same as search except for return type.
    public T Read(Expression<Func<T, bool>> predicate)
    {
        //SingleOrDefault used purposefully instead of FirstOrDefault to cause exception if
        //there is more than one instance that meets the predicate.
        return dbSet.Where(predicate).SingleOrDefault<TEntity>();
    }

    //Search
    public IQueryable<T> Read(Expression<Func<T, bool>> predicate)
    {
        return dbSet.Where(predicate);
    }

    //ReadAll
    public IQueryable<T> Read()
    { }

As mentioned... When separated into separate files, these worked fine as they were explicitly called as needed. But I want to use the Façade pattern to simplify the code.

The problem I am having of course is that there is no polymorphism based on return type. I understand the and fully appreciate reasons for this.

I was thinking of adding an optional bool parameter to the end of the search. But it doesn't feel right for some reason. Something like...

    //Search
    public IQueryable<T> Read(Expression<Func<T, bool>> predicate, bool returnOne = false)
    { }

So my question is, has anyone got ideas on how to get around this limitation?

like image 935
Francis Rodgers Avatar asked Oct 19 '22 20:10

Francis Rodgers


1 Answers

You could rename the single-entity returning Read to ReadSingle, to make it clear that, even though it takes a predicate as a parameter, it only returns a single entity. I'd try renaming first.

If you'd prefer another option, you could differentiate the method signatures in their parameters. You can create a predicate-wrapper type for the single-returning oddball case:

public class ExpressionToFindSingle<T> {
  private Expression<Func<T, bool>> predicate;
  public ExpressionToFindSingle(Expression<Func<T, bool>> predicate) {
    this.predicate = predicate;
  }
  public static implicit operator Expression<Func<T, bool>>(ExpressionToFindSingle<T> wrapper) {
    return wrapper.predicate;
  }
}

Given the conversion operator, you can use the wrapper as its predicate directly. To make things simpler for callers, you can also extend Expression<..> to more easily create this wrapper:

public static class Predicates {
  public static ExpressionToFindSingle<T> ForSingle<T>(this Expression<Func<T, bool>> predicate) {
    return new ExpressionToFindSingle<T>(predicate);
  }
}

Then, your class can have these methods (showing only their signatures):

T Read(ExpressionToFindSingle<T> predicate);

IQueryable<T> Read(Expression<Func<T, bool>> predicate);
like image 141
Jordão Avatar answered Nov 03 '22 05:11

Jordão