Repository and Specification pattern
Asked Answered
S

3

6

I'm currently setting up a new project, and I have run into a few things, where I need a little input.

This is what i'm considering:

  • I would like a generic repository

  • I don't want to return IQueryable from my repository.

  • I would like to encapsulate my queries in specifications.

  • I have implemented the specification pattern

  • It needs to be easily testable

Now this is where I get a little stuck and my question is which way would be the most elegant way of calling the find method with one or more specifications:

(Fluent): bannerRepository.Find().IsAvailableForFrontend().IsSmallMediaBanner()

or express queries as lambdas with my specifications

(Lambda): bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

or maybe some completely other way? Most important thing is, that the guy implementing the MVC front, should have a good intuitive experience of the repository.

What I am hoping to achieve is to keep som flexibility with regard to being able to combine specifications, and give the experience of "filtering" with the specfications, but without leaking an IQueryable to the controller, but more like an ISpecifiable, that only allows to modify the query with specifications and not with Linq. But am i just back at leaking query logic to the controller this way?

Soubrette answered 8/2, 2010 at 12:6 Comment(0)
H
2

I have seen some Fluent API's that uses Properties for specifications, so they don't add the parenthesis noise to the clients.

bannerRepository.Find.IsAvailableForFrontend.IsSmallMediaBanner.Exec()

Being Exec() a method for executing the specifications against the repo.

but even if you don't use the properties, I would go for the fluent API, since it has the minimum noise.

Holladay answered 8/2, 2010 at 12:34 Comment(1)
+1 I ended up doing something like this. This project is in VB.Net so the parentheses aren't an issue, I can omit them. I return af specification object from my Find which has the IQueryable passed to it, and with some Expression manipulation Ands or Ors the specs together, and finally executes.Soubrette
C
2

or maybe some completely other way?

Well, actually I don't get exactly your repository implementation (e.g. what will the method .Find() return?), but I would choose another direction:

public class Foo 
{ 
    public Int32 Seed { get; set; }
}

public interface ISpecification<T> 
{
    bool IsSatisfiedBy(T item);
}

public interface IFooSpecification : ISpecification<Foo> 
{
    T Accept<T>(IFooSpecificationVisitor<T> visitor);
}

public class SeedGreaterThanSpecification : IFooSpecification
{
    public SeedGreaterThanSpecification(int threshold)
    {
        this.Threshold = threshold;
    }
    public Int32 Threshold { get; private set; }
    public bool IsSatisfiedBy(Foo item) 
    {
        return item.Seed > this.Threshold ;
    }
    public T Accept<T>(IFooSpecificationVisitor<T> visitor)
    {
        return visitor.Visit(this);
    }
}
public interface IFooSpecificationVisitor<T>
{
    T Visit(SeedGreaterThanSpecification acceptor);
    T Visit(SomeOtherKindOfSpecification acceptor);
    ...
}
public interface IFooRepository 
{
    IEnumerable<Foo> Select(IFooSpecification specification);
}
public interface ISqlFooSpecificationVisitor : IFooSpecificationVisitor<String> { }
public class SqlFooSpecificationVisitor : ISqlFooSpecificationVisitor
{
    public string Visit(SeedGreaterThanSpecification acceptor)
    {
        return "Seed > " + acceptor.Threshold.ToString();
    }
    ...
}
public class FooRepository
{   
    private ISqlFooSpecificationVisitor visitor;

    public FooRepository(ISqlFooSpecificationVisitor visitor)
    {
        this.visitor = visitor;
    }

    public IEnumerable<Foo> Select(IFooSpecification specification)
    {
        string sql = "SELECT * FROM Foo WHERE " + specification.Accept(this.visitor);
        return this.DoSelect(sql);
    }

    private IEnumerable<Foo> DoSelect(string sql)
    {
        //perform the actual selection;
    }
}

So I have an entity, its specification interface and several implementors involved in a visitor pattern, its repository interface accepting a specification interface and its repository implementation, accepting a visitor capable to translate specifications into SQL clauses (but it's just a matter of this case, of course). Finally, I would compose specification "outside" the repository interface (using fluent interface).

Maybe this is just a naive idea, but I find it quite straightforward. Hope this helps.

Chiquitachirico answered 4/9, 2013 at 16:41 Comment(0)
A
1

Personally I would go with the lambda way. It may be because of my love for lambda but it provides lot's of space for a generic repository setup.

Considering the following:

bannerRepository.Find.Where(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner)

I don't know what your pattern looks like but you could refactor some things here:

Create a generic interface called 'IRepository' of type containing all the methods for data access.

It could look like this:

interface IRepository<T> where T : class
{
    IEnumerable<T> FindAll(Func<T, bool> exp);

    T FindSingle(Func<T, bool> exp);
}   

Create an abstract 'Repository' class implementing this interface:

class Repository<T> : IRepository<T> where T : class
{
    TestDataContext _dataContext = TestDataContext();

    public IEnumerable<T> FindAll(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Where<T>(exp);
    }

    public T FindSingle(Func<T, bool> exp)
    {
        _dataContext.GetTable<T>().Single(exp);
    }
}

We can now create an interface for the banners table/objects which implements our 'IRepository' and a concrete class extending the abstract 'Repository' class and implementing the 'IBannerInterface':

interface IBannerRepository : IRepository<Banner>
{
}

And the matching repository to implement it:

class BannerRepository : Repository<Banner>, IBannerRepository
{
}

I would suggest using this approach as it gives you a lot of flexibility as well as enough power to control all the tiny entities you have.

Calling those methods will be super easy that way:

BannerRepository _repo = new BannerRepository();

_repo.FindSingle(banner => banner.IsFrontendCampaignBanner && banner.IsSmallMediaBanner);

Yes, it means that you have to do some work but it is hell easier for you to change the data source later on.

Hope it helps!

Ambulatory answered 8/2, 2010 at 13:12 Comment(4)
Thanks for you answer. I have the repository bits covered - not unlike your solution. What I was looking for, was more input on how to do the specification pattern with the mentioned syntax.Soubrette
Currently the fluent approach is used very widely and probably preferred and supported by many people. But as said, personally I would go with the Lambda way because to me it really is more readable and looks neat. But this is just my preference :PAmbulatory
I have used a very similar approach to the what you put forward here Shaharyar. The big problem I run into is testability. I have still not found a way in my unit test to verify that a specific linq expression was called. I use NSubstitute and it can't do this. I have also looked in to Moq, and can't find a way to do this.Ankledeep
with this approach, you are loading the entire table and filtering it in memory, since a Func<T, bool> cannot be translated into the "store" (the method Where(Func<T, bool>) is an extension method of IEnumerable<T> not IQueryable<T>). You should use Expression<Func<T, bool>> so you use the method IQueryable<T>.Where(Expression<Func<T, bool>>) that can translate the criteria to the store provider.Mcclimans

© 2022 - 2024 — McMap. All rights reserved.