Friday, August 19, 2011

chain together filters on top of repository calls

In C#, using NHibernate and LINQ, one may chain together filters on top of repository calls like so:

fooModel.SetPagedFoo(FooRepository.GetAll().FindAll(FilterSpecs.BarCriteria(filter)).FindAll(FilterSpecs.BazCriteria(filter)));

 
 

The "filter" being passed in is of this object, but isn't what is really interesting...

using System;

using System.Collections.Generic;

using System.Web.Mvc;

using MyApp.Core;

using MyApp.Core.Entities;

using MyApp.Core.Repositories;

using MyApp.Core.Utils;

namespace MyApp.UI.Models

{

   public class FilterParameters

   {

      public IBarRepository BarRepository { get; set; }

      public IBazTypeRepository BazRepository { get; set; }

      

      public Bar bar { get; private set; }

      public Baz baz { get; private set; }

      

      public ProgramFilterParams(FormCollection formCollection)

      {

         BarRepository = ServiceLocator.Get<IBarRepository>(AppCtxIds.BAR);

         BazRepository = ServiceLocator.Get<IBazRepository>(AppCtxIds.BAZ);

         tryToDefineBar(formCollection["baresque"] as string);

         tryToDefineBaz(formCollection["bazesque"] as string);

      }

      

      private void tryToDefineBar(string potentialBar)

      {

         if (!potentialBar.IsNullOrEmpty())

         {

            IEnumerable<Guid> barIds = new Guid[] {new Guid(potentialBar)};

            bar = BarRepository.GetBar(barIds)[0];

         }

      }

      

      private void tryToDefineBaz(string potentialBaz)

      {

         if (!potentialBaz.IsNullOrEmpty())

         {

            IEnumerable<Guid> bazIds = new Guid[] { new Guid(potentialBaz) };

            baz = BazRepository.GetBaz(bazIds)[0];

         }

      }

   }

}

 
 

...the "FilterSpecs" ARE really interesting!

using System.Linq;

using MyApp.Core.Entities;

using LinqSpecs;

using MyApp.UI.Models;

namespace MyApp.UI.Specs

{

   public class FilterSpecs

   {

      public static Specification<Foo> BarCriteria(FilterParameters filter)

      {

         if (filter.bar != null)

         {

            var dateSpec = new AdHocSpecification<Foo>(

                  f => f.Child.Where(c => c.Bar.Id == filter.bar.Id).Count() > 0

               );

            return dateSpec;

         } else {

            var dateSpec = new AdHocSpecification<Foo>(f => f != null);

            return dateSpec;

         }

      }



      public static Specification<Foo> BazCriteria(FilterParameters filter)

      {

         if (filter.baz != null)

         {

            var dateSpec = new AdHocSpecification<Foo>(

                  f => f.Child.Where(c => c.Baz.Id == filter.baz.Id).Count() > 0

               );

            return dateSpec;

         } else {

            var dateSpec = new AdHocSpecification<Foo>(f => f != null);

            return dateSpec;

         }

      }

   }

}

 
 

OK, at the very first line of code at the top of this posting, where we show off chainability of filters, everything is shoved into a method called SetPagedFoo which might look like this...

private IList<Foo> SetPagedFoo(IQueryable<Foo> query = null)

{

   IList<Foo> foos;

   if (query != null)

   {

      foos = query.ToList();

      
more code follows...

 
 

The NHibernate SQL is crafted at the first line of code in this post, but it is not executed until the last line of code above is run. When an IQueryable<Foo> is cast to another type, then and only then does the would be SQL call become an actual trip to the database. This is the magic of IQueryable!

Finally there is a base class for repositories that all of our repositories (such as FooRepository) inheirt from that contains this method:

private IQueryable<T> GetQuery(Specification<T> specification)

{

   var results = Session.Query<T>().Where(specification.IsSatisfiedBy());

   return results;

}

 
 

That is all there is too it!

No comments:

Post a Comment