Linq.Specifications - The Project

In the past few weeks I have revisited the specification pattern using Linq. I have teased it, toyed with it and tricked it up. And today I announce the public availability of a project demonstrating my current thoughts. You can grab the solution from Google Code at http://code.google.com/p/linq-specifications.

I have no doubt that improvements can and will be made if this generates any wider adoption. For now, it suits what I am currently working on quite nicely. YMMV.

Below is a class diagram of the current core of the project. Use it as a quick reference but be sure to check out the code for a more in-depth view. 

Shortly I will do up a few examples on the project wiki that will flesh out some of the gotchas I have come across already and I need some more testing around some of the elements. For now, however, I just wanted to get this out in the wild. Feel free to comment/flame. I look forward to any feedback.

kick it on DotNetKicks.com

October 18, 2008 10:25 by steven.burman
E-mail | Permalink | Comments (9) | Comment RSSRSS comment feed

Related posts

Comments

November 8. 2008 05:48

Darren

Very good article! I have started using your Linq.Specifications pattern and I have hit a wall when attempting to write a QuerySpecification for a parent - child situation. Here is my Expression method so far:

public override Expression<Func<Source, bool>> MatchingCriteria
{
get { return parent => parent.Name == name && parent.Children....//what next??? }
}

Is this the right way to go about this or should I write two separate QuerySpecifications and combine them somehow?

Darren

November 8. 2008 08:09

Darren

Got it now...

return parent => parent.Name == pname && parent.Children.Any(child => child.Name == cname)

Darren

November 8. 2008 09:58

steven.burman

Yep, that's the approach. Unleash the power of Linq!

I have come across situations (significantly more complex than what you describe) that seem to push the limits of what NHibernate.Linq can resolve. When I get into these situations I inevitably end up do some "post-sql" calculations and asessments - the beauty of the specification pattern is that it is all contained in that specification definitition and the calling code is none the wiser as to the internals.

I feel that the specification pattern has us well placed for future increased maturity of NHibernate.Linq (planned in v2.1) and any future performance optimisations we may see necessary.

steven.burman

November 11. 2008 08:08

Darren

Hi! I was wondering if you have any idea what's happening here in my example. When I run the following code:

var getbyid = new ProductByIdSpecification(3);
var foundProduct = productRepository.FindOne<Product>(getbyid);

I am getting the following exception:

NHibernate.MappingException: No persister for: System.Converter`2

At first I thought it might be something to do with Linq-NHibernate so I tried this (which worked ok):

var foundProduct = (session.Linq<Product>().Where(p => p.Id == 3)).First();

So I am thinking it must be something to do with the call to Convert made in the QuerySpecification class. Am I going down the right path with this? Have you seen this problem before and, more importantly, do you have a solution at hand?

I am using Fluent NHibernate by the way.

Darren

November 11. 2008 21:48

steven.burman

I can't recall to what extent I have used this conversion specification code against NHibernate.Linq but I am sure that my basic tests were successful.

Without seeing your specification it is difficult to know what has gone wrong. From the error that you have reported it is looking like NHibernate is expecting the return type to be a mapped entity. Ensure that the first generic parameter represents the persisted entity as this type is the one that gets passed to session.Linq<T>. From there the conversion process should not affect NHibernate.Linq. (In the test suite, the class AuthorByBlogNameSpecification is the pattern to be following.)

I intend to add a proper data access layer that utilises NHibernate, NHibernate.Linq and FluentNHibernate to the project eventually. When I get around to that I will be able to more robustly test the full integration.

steven.burman

November 11. 2008 22:31

Darren

Thanks Steven. I appreciate your time. I have spend a few hours trying to figure this out already and have had no luck so far. I just can't figure out why there should be a MappingException for the System.Converter delegate which itself should simply return the same mapped entity type of "Product".

I can provide some more details of what I have done below and as you can see it is a very simple example of "GetById" that I am trying to execute. As mentioned previously, I can get this to work using the session object directly and also by using "RetrieveAllSpecification" (probably because in these examples do not need to call the "Convert" method).

So here are some more details...

The specification I am using is a basic "GetById" example that returns the same type as the mapped entity (in this case "Product"). Here is the specification class:

public class ProductByIdSpecification : QuerySpecification<Product>
{
private readonly int id;
public ProductByIdSpecification(int id) { this.id = id; }

public override Expression<Func<Product, bool>> MatchingCriteria
{
get { return p => p.Id == id; }
}
}

This is my calling code:

var getbyid = new ProductByIdSpecification(3);
var foundProduct = productRepository.FindOne(getbyid);

And this is the code in the repository:
public TResult FindOne<TResult>(ISpecification<T, TResult> specification)
{
return FindAll(specification).First();
}

public IQueryable<TResult> FindAll<TResult>(ISpecification<T, TResult> specification)
{
return specification.SatisfyingElementsFrom(session.Linq<T>());
}

The following exception happens when calling Convert (in QuerySpecification<T, TResult> class):

NHibernate.MappingException: No persister for: System.Converter`2
[[Dadou.RepositoryPattern.Model.Product, Dadou.RepositoryPattern.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null],[Dadou.RepositoryPattern.Model.Product, Dadou.RepositoryPattern.Model, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]

Darren

November 12. 2008 15:10

steven.burman

You will have to leave this with me. I can only assume that it is breaking down when NHibernate.Linq attempts to parse the query expression. It must treat the conversion process as part of the parsable expression - which is why it expects it to be a mapped entity.

steven.burman

November 12. 2008 15:26

steven.burman

I have a workaround - and it may well be the only way to ensure robustness in the process. In the QuerySpecification class I replace the SatisfyingElementsFrom code from:

public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates)
{
if (MatchingCriteria != null)
return candidates.Where(MatchingCriteria).Convert(ResultMap);

return candidates.Convert(ResultMap);
}

to:

public virtual IQueryable<TResult> SatisfyingElementsFrom(IQueryable<T> candidates)
{
if (MatchingCriteria != null)
return candidates.Where(MatchingCriteria).ToList().ConvertAll(ResultMap).AsQueryable();

return candidates.ToList().ConvertAll(ResultMap).AsQueryable();
}

Explicitly calling ToList() executes the query and then the conversion process is done post sql execution. I think this is the right way to do this, but I will ponder it for a while. I have checked in the change to Google Code.

steven.burman

November 12. 2008 20:38

Darren

Thanks Steven for the workaround which does indeed work! Thanks also for the explanation which makes it clear to me now why it was not working before.

Darren

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

July 4. 2009 20:12