NHibernate Forge
The official new home for the NHibernate for .NET community

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.


Posted dic 17 2009, 01:00 a.m. by Steve Strong
Filed under:

Comments

uberVU - social comments wrote Social comments and analytics for this post
on 12-16-2009 23:55

This post was mentioned on Twitter by nhibernate: Blogged: Linq to NHibernate Progress Report - A Christmas Gift?: Time for another progress report, and this one's a... http://bit.ly/8DNuRz

Stefan Simroth wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 12-18-2009 13:00

Great work! I just tried out the NH Linq provider (the old one) and I am very happy with it! So happy that I immediately blogged about it, in order to convince my team members to use this on our project. And I'll move to the new provider asap.

Keep up the good work!

Stefan

www.empinia.org/.../100

credit repair nh wrote credit repair nh
on 01-04-2010 23:37

I can\'t believe I haven\'t been to this website before!

William Chang wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 01-08-2010 0:55

Wow! Awesome!

One request (don't know it's been done because I had to use HQL to make it happen), partial property retrieval using projections like:

var partialCustomer = from c in db.Customers

select new Customer { CustomerID=c.CustomerID, City=c.City };

Using Linq to NHibernate for simple things are great! I wish to do complex queries using Linq over ICriteria/HQL/QueryOver. QueryOver is nice, but it feels too verbose when typing and reading a complex query compare to Linq (easier and simpler to follow).

I highly appreciate all the hard work supporting Linq for NHibernate. Please keep up the good work and your contributions to NHibernate.

Thank you!

Odul Kanberoglu wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 01-18-2010 11:36

I have converted the project I have been working on, to the new implementation of NHibernate Linq. Conversion went smooth as expected. However, later I had problems with generated expressions for querying the database. I cannot confirm if the same problem occured with the previous implementation of NHibernate Linq.

When an expression is formed with a lamda expression in the form

Expression<Func<Province, bool>> exp = p => p.CountryId == country.Id;

where both the CountryId property of the type Province and the Id property of the type Country are of type "byte".

C# compiler prepares the lambda expression into an expression including the casting of the byte property value and the constant "country.Id" to Int32.

{p => (Convert(p.CountryId) = Convert(value

(AddressToGo.Business.Default.AddressComponents+<>c__DisplayClass0).country.Id))}

When NHibernate Linq prepares the command to execute for retrieval of data by using this expression, parameter for CountryId is generated of type "byte". When the value from the constant expression country.Id is assigned, an exception is thrown since the constant value is of type "Int32".

The expression can be prepared manually without using a lambda expression and works perfectly. However the amount of code written is larger when compared to a simple lambda expression.

ParameterExpression prm = Expression.Parameter(typeof(Province), "p");

 Expression<Func<Province, bool>> exp =

     Expression.Lambda<Func<Province, bool>>

     (

       Expression.Equal

       (

         Expression.MakeMemberAccess(prm, typeof(Province).GetProperty("CountryId")),

         Expression.Constant(country.Id, typeof(byte))

       ),

       prm

     );

I have started topics about the case on MSDN forums and stackoverflow.

stackoverflow.com/.../lambda-expression-compilation

social.msdn.microsoft.com/.../379d431f-3cfc-4751-8401-31ebd353aaf7

Steve Strong wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 01-18-2010 12:09

Odul,

Gotta love the compiler :)  It shouldn't be too difficult within the provider to detect scenarios such as this - I'll add it to the list.  For now, you'll have to run with the expression tree that you're building by hand, but it does suck quite a lot.

Cheers,

Steve

Andrey wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 01-28-2010 9:48

Excellent work!

Question: it will be supported?:

.OrderBy(x => x.Code.ToString())

I expect to see:(SQL)

... FROM .... ORDER BY (CAST [Code] as varchar)...

Thank you!

Hainesy wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 05-13-2010 6:12

Steve

I realise this is a bit late on this post, but I'm struggling to get my hands on the new Linq code.

There's no Query() method off my session...

Is it in NHibernate 2.1.2.4000? This is the release I'm working off and unfortunately due to dependency hell it's going to be a struggle to upgrade at the moment.

Yesterday's news wrote What's new in NHibernate 3.0 GA
on 12-04-2010 23:07

What's new in NHibernate 3.0 GA

Sulik wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 04-11-2011 6:29

Very helpful

RutB wrote re: Linq to NHibernate Progress Report - A Christmas Gift?
on 01-18-2012 12:09

This post is exactly what i was looking for, but unfortunately it doesnt works for me :-(.

I'm getting the error "The method or operation is not implemented". (We work with nHibernate 3.2)

Does anyone have an idea what can be the reason?

(To be specific, I'm getting the error when I adds the "into" expression, regular "join" with one key works fine)

Thanks in advance,

click through the next document wrote click through the next document
on 10-08-2014 15:29

Linq to NHibernate Progress Report - A Christmas Gift? - NHibernate blog - NHibernate Forge

Powered by Community Server (Commercial Edition), by Telligent Systems