Adding Linq support to NPersist

I’m currently adding some real Linq support to NPersist.
I did start on it when the first public previews of Linq was released, but then I was kind of stuck in World of Warcraft for a while (OK, slightly more than a while).
But I’ve finally broken out of the world of Azeroth and I’m back in the real world again.

So, here is a few sneak peeks of what will be possible:

 var res = from cust in ctx.Repository<Customer>() 
          where (from order in cust.Orders 
                 where order.OrderDate == 
                 new DateTime(2008,01,01) && order.Total == 3.1 
                 select order).Count > 0 
          select cust;

 The Linq query will be turned into our own DSL NPath :

select * 
from Customer 
where ((( 
	select count(*) 
	from Orders 
         where ((OrderDate = #2008-01-01#) and (Total = 3.1)) 
        ) > 0))

The NPath query will then be transformed into SQL once the user tries to access the result.
Sure, it is some slight overhead to transform a 2nd query language, but the NPath parser is extremely fast, and NPersist use NPath internally so we just wanted to avoid making two SQL generators.

The SQL generation is by far the biggest beast inside NPersist, it’s big, furry and very angry, so we just want to stay away from it if we can. ;-)

We have also managed to solve the problem with Linq and Load spans that have haunted us since we first saw Linq.
“How the heck do we add load spans to the select when we cannot change the syntax of the select part?”
The solution is quite simple: “You don’t”
Instead, we pass the loadspan to the query source, like this:

var res = from cust in ctx.Repository(new LoadSpan<Customer> 
("Name","Email","Address.StreetName"))    

          select cust; 

This way we do not need to invent weird hacks that don’t really fit into the Linq syntax.
The solution might have been obvious for others, but we were really stuck on it, pretty much because we have always specified load spans in the select clause of NPath.

3 Comments

  1. Frans Bouma says:

    That prefetch path/span solution isn’t going to work if you formulate two queries using the same context, or are these spans stored inside the queryable produced by the Repository call and not inside the context?

    I use a similar approach but with an extension method:
    var q = (from c in metaData.Customer select c).WithPath(
    new PathEdge(c=>c.Orders, o=>o.EmployeeId==3), …

    the thing is with this you can specify nested edges on the fly and also specify filters with the edge.

    One disadvantage is that if this query is inside a join branch, the whole path doesn’t make sense. This is the fault of the developer so ignoring it or throwing an exception is best.

    It’s impressive how fast you got this working, I struggled for months with the provider, as there was no documentation about anything and I couldn’t pass on the elements in a query which looked very similar to the linq query, unfortunately.

    Once a core system of a linq provider is in place the rest is filling in the blanks but don’t underestimate the fine print. There is a lot of time to waste on tiny little details, like all the different query forms ‘Contains’ can be used in, which results in nested aggregates and a lot of derived tables.

  2. Roger Alsing says:

    >>or are these spans stored inside the queryable produced by the Repository call and not inside the context?

    Exactly.
    Storing query related info in the context would be madness :-P

  3. Frans Bouma says:

    “Storing query related info in the context would be madness ”
    I know about a given O/R mapper, something with Linq and Sql, which does that ;) :).

Leave a Reply to Roger Alsing Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s