views:

95

answers:

2

Hi Guys,

Trying to make a really simple repository and service layer pattern here. (.NET 4, C#, LINQ, although this question is partially language-agnostic). Note: this is just R&D.

My goal is to minimize the amount of method definitions in my service layer.

Here's my Repository Contract:

interface IFooRepository
{
   IEnumerable<Foo> Find();
   void Insert(Foo foo);
   void Update(Foo foo);
   void Delete(Foo foo);
}

Nothing new there.

Now, here's what im (trying) to have in my Service Contract:

interface IFooDataService
{
   public IEnumerable<Foo> Find(FooSearchArgs searchArgs);
}

Essentially, any particular "Foo" has many properties (id, name, etc), which i would like to be able to search upon.

So, i dont want to have 1x Find method for each different property, i just want one - that way when i create extra properties i dont have to modify the contracts.

The "FooSearchArgs" is just a simple POCO with all the different "Foo" properties it.

So, that's what im trying to do, here's my questions:

  • Is this poor design? If so, what are the alternatives?
  • How can i implement this filtering in the service layer? Would i have to check what properties of "FooSearchArgs" are set, then keep filtering down? (if this, then query.where, if this, query.where, etc) Anyone have an idea of a clever LINQ IEnumerable extension method to do this? (ie repository.WhereMeetsSearchCriteria(fooSearchArgs))

Appreciate the help.

A: 

Is passing a Func as a parameter to your service layer's Find method, instead of the FooSearchArgs, an option? Enumerables have a Where method (linq) that takes a Func as a parameter, so you could use it to filter the results.

Alka
Its an option, yes - im still learning expression trees/lambdas. Any chance you could expand your answer with an example (relevant to my above question)? Thanks
RPM1984
This could be inefficient though if not using Linq as you could return more results from the database than required.
Craig
@Craig im using LINQ. Only have a fake repository at the moment, but the real one will be either L2SQL or EF.
RPM1984
Something like sLayerInstance.Find(theFoo => theFoo.id%2 == 0). It does nothing interesting but it's an example of what you can do. Then you either use it to filter the repositories Find().
Alka
@Alka - i know how to pass a predicate to the method, i was more looking for guidance as to actually implement the method.
RPM1984
+2  A: 

We use something very similar. One thing you need to decide on is if you are going to expose IQueryable outside of the repository. Your find method returns IEnumerable which could be the IQueryable returned from your when clause.

The advantage of returning the IQueryable is that you can further refine your criteria up outside of your repository layer.

repository.Find(predicate).Where(x => x.SomeValue == 1);

The expression will only be compiled when you come to use the returned data and here in lies the disadvantage. Because you only hit the database when you actually come to use the results you could end up trying to call the database after your session (nhibernate) or connections have been closed.

My personal preference is to use the specification pattern where you pass your find method an ISpecification object is used to do the query.

public interface ISpecification<TCandidate>
{
    IQueryable<TCandidate> GetSatisfyingElements(IQueryable<TCandidate> source);
}

public class TestSpecification : ISpecification<TestEntity>
{
    public IQueryable<TestEntity> GetSatisfyingElements(IQueryable<TestEntity> source)
    {
        return source.Where(x => x.SomeValue == 2);
    }
}

public class ActiveRecordFooRepository: IFooRepository
{
    ...

    public IEnumerable<TEntity> Find<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        ...

        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).ToArray();

        ...
    }

    public TEntity FindFirst<TEntity>(ISpecification<TEntity> specification) where TEntity : class 
    {
        return specification.GetSatisfyingElements(ActiveRecordLinq.AsQueryable<TEntity>()).First();
    }
}

After the query is run the repository calls ToArray or ToList on the resulting IQueryable returned from the specification so that the query is evaluated there and then. Whilst this may seem less flexible than exposing IQueryable it comes with several advantages.

  1. Queries are executed straight away and prevents a call to the database being made after sessions have closed.
  2. Because your queries are now bundled into specifications they are unit testable.
  3. Specifications are reusable meaning you don't have code duplication when trying to run similar queries and any bugs in the queries only need to be fixed in one place.
  4. With the right kind of implementation you can also chain your specifications together.

repository.Find(
    firstSpecification
        .And(secondSpecification)
        .Or(thirdSpecification)
        .OrderBy(orderBySpecification));
Bronumski
An interesting take. However i prefer to use deferred execution (LINQ), as it gives much finer control over the queries (allowing me to build them up in the service layer). Closed session calls are not an issue as ill using another design pattern to combat that (ie UnitOfWork). Thanks for the answer though (+1) - ill have a read of the specification pattern.
RPM1984
You may find this interesting then: http://huyrua.wordpress.com/2010/07/13/entity-framework-4-poco-repository-and-specification-pattern/It is an example of how to implement a persistence ignorant repository using the specification and unit of work patterns.
Bronumski
And this one: http://www.kitchaiyong.net/2009/10/repository-specification-unit-of-work.html. This one goes into a little more depth.
Bronumski
Thanks for the links!
RPM1984
@Bronumski - i ended up applying the specification pattern like you said (using the second link you sent me, which is absolute gold). Works really well, and still enables deferred execution. Thanks again.
RPM1984