Linq to NHibernate Progress Report - A Christmas Gift?

Time for another progress report, and this one's a biggie :)

Barring a couple of pretty minor things that won't take much fixing, all the original Linq tests have now been ported over to the new provider and are all passing. That means the new provider is now (from the perspective of the tests, at least) in better shape that the version 1 provider. It can do everything the original provider could, plus a whole bunch more. A couple of example queries that now work just fine are:

  from e in db.Employees
  from et in e.Territories
  where e.Address.City == "Seattle"
  select new {e.FirstName, e.LastName, et.Region.Description};

  from c in db.Customers
  join o in db.Orders on c.CustomerId equals o.Customer.CustomerId into orders
  select new {c.ContactName, OrderCount = orders.Average(x => x.Freight)};

  from o in db.Orders
  from p in db.Products
  join d in db.OrderLines on new {o.OrderId, p.ProductId} equals new {d.Order.OrderId, d.Product.ProductId}
   into details
  from d in details
  select new {o.OrderId, p.ProductId, d.UnitPrice}

  from c in db.Customers
  join o in db.Orders on c.CustomerId equals o.Customer.CustomerId
  group o by c into x
  select new { CustomerName = x.Key.ContactName, Order = x }

(ignore whether those queries make any real sense, it's just the form of them that matters)

So, more importantly, what doesn't work? Well, out of the tests that we've currently got, not a huge amount. Some important areas that are missing are:

  • Nested selects to produce hierarchical output. I've got some prototype code for doing this, so it will be supported at some point but isn't there right now. Of course, since NH already understands relationships, nested selects are far less important than they are for something like Linq to SQL.

  • Group joins that produce hierarchical output - these essentially boil down to the same code as the nested select case, so support for these will probably come at around the same time. Group joins that don't introduce a hierarchy should work just fine.

  • Set operations, such as Union and Intersect. Union you can obviously do yourself in client code with no particular overhead. Intersect would really be better in the provider :)

  • Left outer join style queries, such as:

    from e in db.Employees
    join o in db.Orders on e equals o.Employee into ords
    from o in ords.DefaultIfEmpty()
    select new {e.FirstName, e.LastName, Order = o};

    This is a fairly widely used construct, so is close to top of the list for future support

  • Let expressions, such as:

    from c in db.Customers
    join o in db.Orders on c.CustomerId equals o.Customer.CustomerId into ords
    let z = c.Address.City + c.Address.Country
    from o in ords
    select new {c.ContactName, o.OrderId, z};

    Again, this is quite high on the TODO list.

  • Support for custom functions. This is actually fully implemented internally, but I just want to review the API usage before I tell you all how to use it - it probably needs a little cleanup from it's current state, but I don't anticipate any major changes.

I've also got a number of TODOs, but mainly cleanup rather than functional, plus I need to work through the error handling to ensure that any queries that are passed in that the provider can't handle are rejected gracefully rather than just barfing (which is the likely case right now).

The only gotcha that I know of is a query that runs just fine but doesn't necessarily return the correct number of results. Specifically, queries like this:

  from user in db.Users
  select new
  {
    user.Name,
    RoleName = user.Role.Name
  };

Right now, this generates a join to the Role table, so any users that don't have a role are not returned. This differs from Linq to SQL where a left join is generated, giving a null RoleName for any users without a role. I believe the Linq to SQL implementation to be correct - the above query doesn't explicitly have any form of filtering (where clause, join clause etc), so you should get back all the users in the database. This one is top of the list, and hopefully will be fixed soon (it's not hard, I've just run out of time!).

In summary, I'm getting pretty happy with the state of the provider and think that it's now ready for general usage - although there are still some important query forms to support, I think there's sufficient there now to do useful work. If you've got good test coverage, then I'd even be happy for it to go live. If you don't have good coverage, then don't come crying to me :)

Of course, this is all in the trunk, so anyone wanting to play either needs to get the trunk source and build it, or take the much easier option of having Horn do the work. Horn builds the trunk on a daily basis, so look for a package built after around 2300GMT on the 16/12/2009 (the package URL on Horn has the datetime stamp in it, so it's pretty easy to spot).

I'm on holiday for the next couple of weeks, and will only have intermittent internet access. However, ping me either by email or twitter with comments / bugs / suggestions, and I'll do my best to reply. Normal service (whatever that is) will return around Jan 4th.

Comments

#  Twitter Trackbacks for Linq to NHibernate Progress Report - A Christmas Gift? [imeta.co.uk] on Topsy.com
Gravatar
Twitter Trackbacks for

Linq to NHibernate Progress Report - A Christmas Gift?
[imeta.co.uk]
on Topsy.com
Left by Pingback/TrackBack on 12/16/2009 11:39 PM
# Social comments and analytics for this post
Gravatar This post was mentioned on Twitter by srstrong: Just posted Linq to NH update http://bit.ly/8rS4u1 - this one is worth a read...
Left by uberVU - social comments on 12/16/2009 11:55 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Don't get too excited, but I think I love you!
Left by Craig on 12/17/2009 12:05 AM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar get excited, because i do love you. thanks so much steve! can't wait to drop this into a branch and test it out
Left by Darren Kopp on 12/17/2009 1:58 AM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar This is really great one.
I have tried it. It promised great time and got me some bites.

While I missed "Expand" when using "Query<>()" instead of "Linq<>()", This helped me discover "QueryOver<>()" which looks like a true treasure.
The only thing I'd miss about it is "Count()" method. Say I want to use the same QueryOVerBuilder to get count of total pages first then apply "Take" and "Skip" to get the page I want.

Also, the API is sometimes confusing. If I use ".OrderBy(...).Asc/Desc" and I have another sort element, should I call ".OrderBy(...).Asc/Desc" again (the first thing I thought about, very Linq standard syntax) or use ".ThenBy(...).Asc/Desc" ??
Same applies to "Where" and "And".

But in general great work. I'm trying to get a working FluentNHibernate build with it to see the real usage in action.
Left by Mohamed Meligy on 12/17/2009 7:09 AM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Well done dude!

Have a great holiday
Left by HHariri on 12/17/2009 7:56 AM
# Reflective Perspective - Chris Alcock &amp;raquo; The Morning Brew #500
Gravatar Reflective Perspective - Chris Alcock &amp;raquo; The Morning Brew #500
Left by Pingback/TrackBack on 12/17/2009 9:33 AM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar I must be going daft. I cant seem to find where to download it from. The horn link doesn't seem to have your dll in there, and i dont know what your svn url is. I am looking forward to having a play with this!
Left by steve on 12/17/2009 10:41 AM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar "This differs from Linq to SQL where a left join is generated, giving a null RoleName for any users without a role. I believe the Linq to SQL implementation to be correct "
if fk field is nullable then emit join towards pk side (left or right) else emit equ join (inner) ;)

Intersect and Except: these are related: create 1 method which produces an Exists query and use it in both with one needing NOT.

Big problems in Linq are Contains(), multiple aggregates in group by results all over the place, nested where / order by between joins with defaultifempty, multiple joins with seamingly the same anonymous typed elements in on clause.

I'd say that group by and join (with groupjoin) are the toughest elements. There's no end to the list of edge cases which might break the code handling these suckers.
Left by Frans Bouma on 12/17/2009 10:45 AM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Now that we have a fairly stable LINQ provider. What would the effort be to allow for a SQL to LINQ transformer?

A lot of developers are more familiar with writing SQL than LINQ. It would be great if there was a tool that would allow for that conversion process.
Left by Matt on 12/17/2009 3:15 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Steve,

The new provider is part of the NH trunk, so it's just within the standard NHibernate.dll assembly. The svn repository can be found here nhibernate.svn.sourceforge.net/svnroot/nhibernate/

Cheers,

Steve
Left by Steve on 12/17/2009 11:39 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Frans,

Thanks once more for your useful hints on this stuff! What you've described is pretty much exactly how I was planning to handle those bits; it's nice to get confirmation that I'm not about to go off on one. And I agree with group by and group join being the real PITA's :)

Cheers,

Steve
Left by Steve on 12/17/2009 11:41 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Matt,

I'm not really sure what you mean - if you want to run regular SQL code, you already can (pretty much all ORMs support this in some way). I'm not sure what additional value a SQL to Linq transformer would give, other than burning a few more CPU cycles - after all, ultimately the Linq query needs to end up back as SQL before it can be handed to the database.

Cheers,

Steve
Left by Steve on 12/17/2009 11:44 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar I just mean for some people that are new to LINQ, it is easier to express what they want via SQL. If they can provide the SQL and then somehow the LINQ expression is generated that would be cool.
Left by Matt on 12/18/2009 1:04 AM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Steve, you can download the trunk built from http://hornget.net
Left by Hadi Hariri on 12/18/2009 3:29 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Now that's a Christmas gift! :)

Thanks.
Left by Arnis L. on 12/24/2009 12:47 PM
# Changes as of January 2010
Gravatar Changes as of January 2010
Left by Hadi Hariri's Blog on 12/26/2009 8:04 PM
# Changes as of January 2010
Gravatar It&amp;rsquo;s with a bittersweet feeling that I write this post. As of January, I will no longer be with
Left by Hadi Hariri on 12/26/2009 8:09 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Hi Steve,

Will the linq provider support querying by the date part of a column, ignoring the time part? The following query works with LinqToSql

var query = from rfq in db.RequestForQuotes where rfq.DateReceived.Date == new DateTime(2009, 12, 9) select rfq;

But using the latest trunk build from horn (nhibernate-trunk-03-01-10-201504.zip) gives the exception:

NHibernate.QueryException: could not resolve property: Date of: TestNHibernate.RequestForQuote [.Where(NHibernate.Linq.NhQueryable`1[TestNHibernate.RequestForQuote], Quote((rfq) => (DateTime.op_Equality(rfq.DateReceived.Date, p1))))]
Left by Chris Richards on 1/4/2010 2:18 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Chris,

There's no support for the various properties on a DateTime, but these will added sometime soon. In addition, there will be a public extensibility mechanism so that you can support properties on other types that the provider doesn't know about.

Cheers,

Steve
Left by Steve on 1/7/2010 5:25 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Hi Steve,
I found two problems while upgrading from old Linq to new one.

var resault = db.PointsOfInterest.Where(poi => poi.Title.Contains(keyword)).Take(limit).OrderBy(poi => poi.Title);

Breaks with OrderBy -> System.NotSupportedException: Specified method is not supported. But if Take is last called, returns collection:

var resault = db.PointsOfInterest.Where(poi => poi.Title.Contains(keyword)).OrderBy(poi => poi.Title).Take(limit);

Second one, when will be support for ToList() or ToArray(). It breaks when is called ToList() with System.NotSupportedException or is there another way to convert Queryable to List.

Thanks

Left by xajler on 1/8/2010 3:35 PM
# re: Linq to NHibernate Progress Report - A Christmas Gift?
Gravatar Xajler, thanks for the comment. I'm not that surprised by the ordering of Take & OrderBy, but it's definitely a bug so I've added it to my backlog. At least the workaround is simple :)

What issue are you having with ToList()? I use it all over the place... if you can send me a failing query + the exception, that would be useful.
Left by sstrong on 2/25/2010 12:18 PM

Leave Your Comment

Title*
Name*
Email (never displayed)
 (will show your gravatar)
Url
Comment*

Please add 6 and 7 and type the answer here:

Preview Your Comment.