views:

426

answers:

2

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?

A: 

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.

David Lay
+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.
Luhmann
A: 

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!

Shaharyar
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.
Luhmann
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 :P
Shaharyar