What would NHibernate ICriteria look like in .net 3.5?

If NHibernate decided to ditch compatibility with plain old .net 2.0 and focus on 3.5 how would the ICriteria interface change? Previously I was throwing around an idea of using a simple lambda expression to resolve the property name. Well, I couldn't help but build on this a little more. The following idea is not supposed to be LINQ, that would be far more complicated and LINQ is essentially its own interface, which is not the point. The point is, if ICriteria was written today in .net 3.5, what could it look like? How could it change?

So just to recap, the original code sample looked like:

ICriteria c = session.CreateCriteria(typeof(Person));
c.Add(Restrictions.Eq(Property.GetFor(() => new Person().FirstName), "John"));

Paul later come back and suggested I try using a slightly different signature so that the "Person" object doesn't need to be instantiated for no reason, this means the we can do this:

ICriteria c = session.CreateCriteria(typeof(Person));
c.Add(Restrictions.Eq(Property.For<Person>(p => p.FirstName), "John"));

I like this a lot more. So this is the point where I started playing around and wanted to try and implement a neater way of integrating this to add Criterion. First off I tried implementing ICriteria.Add(Restrictions.Eq()) as an expression which looked like:

ICriteria c = session.CreateCriteria(typeof(Person));
c.Add(RestrictBy.Eq<Person>(p => p.FirstName == "John" ));

That is where I got up to in the previous post. So after this I started implementing some of the other functions found on the 'Restrictions' class such as:

  • .NotNull()
  • .IsNotNull()
  • .Not()
  • .Between()
  • .Gt() (Greater than)

Then I realized all of the above functions are able to be figured out using the syntax I already had: 'p.FirstName == "John"', so how about p.FirstName != "John", or p.ID > 0 or p.FirstName != null, you get the idea. This being the case, now there is no need for the Restrictions class at all, nearly everything can be figured out by using Add(). The other problem I wanted to solve was not having to keep passing the generic <Person> class in all the time. So I created a class which wraps ICriteria and keeps track of these few things, so now it looks like:

ICriteria c = session.CreateExpression<Person>()
.Add(p => p.FirstName == "John")
.Criteria;

Most of the other functions on the Restrictions class can be added the same way:

ICriteria c = session.CreateExpression<person>()
.Add(p => p.FirstName == "John") //Restriction.Eq()
.Add(p => p.LastName != null) //Restriction.IsNotNull()
.Add(p => p.ID > 0 && p.ID < 1000) //Restriction.Between()
.Criteria;

There are also other more complex things that the ICriteria interface does such as adding Projections and Joins. So how would the old AddAlias() function work? Like this perhaps:

ICriteria c = session.CreateExpression<Person>()
.Alias<Address>(p => p.Addresses, "addr")
.Add(a => a.Postcode != null)
.AddAndReturn(a => a.Address2 == null)
.Criteria;

Bringing it all together, here's a comparison between the old interface and the expressions version.

Current ICriteria:

ICriteria o = session.CreateCriteria(typeof(Person))
.Add(Restrictions.Eq("FirstName", "John"))
.CreateAlias("Addresses", "addr")
.Add(Restrictions.IsNotNull("addr.Postcode"))
.Add(Restrictions.IsNull("addr.Address2"))
.AddOrder(Order.Asc("ID"));

Same query using Expressions:

ICriteria c = session.CreateExpression<Person>()
.Add(p => p.FirstName == "John")
.Alias<Address>(p => p.Addresses, "addr")
.Add(a => a.Postcode != null)
.AddAndReturn(a => a.Address2 == null)
.OrderAsc(p => p.ID)
.Criteria;

In conclusion, this is probably not going to revolutionize the global economy, but on some levels I think it feels a little more intuitive and a little more modern. In other respects, it's probably not much less typing then the original. Also with the up and coming linq provider, is this a waste of time? or does it complement it?

Feel free to have a poke around the source, and corresponding tests

.NET Code Snippets NHibernate
Posted by: Brendan Kowitz
Last revised: 21 Sep 2013 12:15PM

Comments

4/18/2009 2:05:38 PM
Looks so great... but how about or expression. For example:
.Add(p => p.ID > 0 || p.ID < 1000)
or also
.Add(p => p.ID >= 0 && p.ID <= 1000)

Did you support it? it there any way to do that better than using NHibernate magic string?

Thank you very much.
6/25/2011 3:57:27 AM
order cheap pill http://ordercheappill.wordpress.com

No new comments are allowed on this post.