> Hey, I'm not saying everything is a simple patch. Grouping and joining is
> hard stuff that should be at the center of the design. And, if I may
repeat
> myself, having a list to discuss stuff first is essential. But things such
> as the LIKE discussion we've had before may well be a simple patch.
oh I agree. Similar to the thing we saw last week with the
skip/take/count stuff.
> I just think that someone who has built a complete provider himself may
have
> a wrong impression as to how hard it would be to get into NH's LINQ code,
> that's why I explained its relationship to re-linq. Right now, L2NH has
> about 4500 LoC + about 9000 LoC in unit tests. Compare that to DbLinq or
> your own. Or to re-linq for that matter, which has > 13 KLoC in the
frontend
> (used by NH), 10 KLoC in the SQL generation backend (not used by NH), and
> 20KLoC in unit tests for frontend and backend each. And the fun only
begins
> when you actually look at the complexity of each piece of code.
LoC measured in ndepend (so true LoC) or from sourcefiles? My linq
provider has in ndepend 6500 LoC. In sourcecode, it's a multiple of that,
but I'm very verbose haha :D
But kidding aside, I see the advantage re-linq brings, no question
about that, I just wonder (and still do) where the complex linq stuff is
solved: in re-linq or does a user of re-linq have to solve these probs?
I'll have a look at re-linq to see what kind of trees it produces
with some of the 'pain' linq queries like: (adventure works)
var q = from reason in metaData.Reason
join time in
(
from timeHeader in
(
from timeHeader in
(
from
timeHeader in metaData.NonPresentTimeHeader.Where(header => header.Id == 6)
join
userDetail in metaData.UserDetail on timeHeader.UserId equals userDetail.Id
into userJoin
from user in
userJoin.DefaultIfEmpty()
select
timeHeader
)
join userDetail in
metaData.UserDetail on timeHeader.ApprovedFromId equals userDetail.Id into
approverJoin
from approver in
approverJoin.DefaultIfEmpty()
select timeHeader
)
join time in metaData.NonPresentTime on
timeHeader.Id equals time.HeaderId into timeJoin
from joinedTime in timeJoin.DefaultIfEmpty()
select joinedTime
) on reason.Id equals time.ReasonId into reasonJoin
from joinedReason in reasonJoin.DefaultIfEmpty()
select new
{
ReasonID = reason.Id,
Reason = reason.Reason,
HeaderID = joinedReason.HeaderId ?? -1,
TimeID = joinedReason.Id,
Notes = joinedReason.Notes,
DateStart = joinedReason.DateStart,
DateEnd = joinedReason.DateEnd
};
it still pains me to see this one fail in my linq provider (among
several other 'headache' queries), but then again, it's not a common query
;) (it's a reproduction of a problem with a real-life query, so it doesn't
make sense, but illustrates a couple of nasty issues)
the above misery query has several complex problems combined which
make it problematic to work with. What would be great if a pre-processor
would solve these, so transformation is easier (to sql): that elements are
at the right spot for discovery for transformation, so the provider doesn't
have to hunt down sources for particular properties, can work with scopes
easily so subtree references are not crossing scopes for alias assignment
etc.
If re-linq can solve that, it would indeed be rather 'easy' as in:
way easier than creating it using the 'warren' method with expression
objects which are converted into other expression objects etc. which leads
to painful conversions.
FB
> Our intention was to keep the hardest stuff in the lowest layers, because
they
> have a higher chance of being reused. (E.g., in our first version, we had
> backtracking of transparent IDs in the back end, which would have meant
that
> every backend would have to handle that.)
>
> The first part of Steve's project, moving HQL from string-based to AST-
> based, might have been the harder part for all I know.
>
> As for your detailed questions... well... I understand re-linq's
> architecture when Fabian explains it to me ;-) But I better let him fill
in
> the details.
>
> Cheers,
> Stefan
>
> > -----Original Message-----
> > From: [email protected] [mailto:nhibernate-
> > [email protected]] On Behalf Of Frans Bouma
> > Sent: Thursday, July 29, 2010 11:06 AM
> > To: [email protected]
> > Subject: RE: [nhibernate-development] NH-2254
> >
> > > > From: [email protected] [nhibernate-
> > > > [email protected]] on behalf of Frans Bouma
> > [[email protected]]
> > >
> > > > Writing a linq provider takes a lot of dedication and
> > focus, you
> > > > can't just 'jump in and provide a patch', that's not going
> > anywhere. You
> > > > have to design the thing from start to finish, write tools to help
> > you
> > > > develop the thing (like visualizers which view the various stages
> > of the
> > > > expression tree), and implement the various stages. You however
> > seem to
> > > > think it's a matter of 'someone will come up with a patch for
> > <feature>
> > /
> > > > <bug>'. No way in hell that that's going to work for the linq
> > provider.
> > >
> > > Frans, if you want to understand the dynamics of development here
> > you'll
> > > have to take a look at the code. Writing a LINQ provider based on
> > > re-
> > linq
> > is
> > > a very different endeavor than starting from scratch. You did it the
> > Matt
> > > Warren way, basically, right?
> >
> > yep
> >
> > > With re-linq, you get a nice AST and some tools. No IQueryable-based
> > > expression tree, no transparent
> > identifiers,
> > etc.
> >
> > that's nice :) I hope that it solves the problem of encapsulated
> > sources (where a source in a join is converted into a property of an
> > anonymous type, which is accessed in another join, which is then again
> > made a property of an anonymous type. By that time, the original
> > source is lost, you have to track it in a tracker to be able to assign
> > the right aliases, as the complete join is made 1 join list in the
> > output. With a system which makes this possible without hassle, a LOT
> > is gained. Not sure if re- linq does that though (haven't looked at it
> > yet)
> >
> > > And I understand that HQL is closer to LINQ than SQL is to LINQ, so
> > some
> > > transformations are simply not necessary. I think the hardest part
> > > is designing how a certain LINQ expression should be translated to
> > > HQL -
> > that's
> > > why I keep insisting on the HQL output for diagnostics. (Now that's
> > just
> > > theory, and I'm sure Steve can tell us about some monumental
> > > problems
> > he
> > had
> > > to solve, but I still think that you can't judge the accessibility
> > > of
> > that
> > > code from your own experience. The parts of the LINQ2NH code that I
> > looked
> > > at don't look that frightening after all, and that's a good thing!)
> >
> > tools which visualize the tree's state on the various stages
> > during transformation is essential, as it can quickly show you where
> > you did or didn't do the right thing.
> >
> > But even with an AST, a lot of problems still remain. For
> > example the query folding with group by (as group by is outside the
> > query it works on in linq, in SQL it's part of the same query),
> > multi-aggregate queries which require query folding (query becomes
> > derived table/subquery of subsequential aggregate's source, with value
> > passing in projection)...
> > these
> > problems are still on your plate (to name a few). unless you've solved
> > these as well (which would be great :))
> >
> > > Long story short, I believe that once you understand what
> > transformation
> > the
> > > code is trying to achieve, any good coder should be able to create a
> > little
> > > patch.=
> >
> > Yes, if one understands the code, creating a patch isn't that
> > hard.
> > Getting there however is something else. I simply don't believe
> > stories where people say they understand just 'a part' of a linq
> > provider and can make proper decisions about where to change which
> > code to add a feature.
> > Sure, the easy stuff, like a from + a where and an entity returning
> > select, or like the many 'full' (read: fall flat on your face if you
> > do something
> > complex) linq providers out there which simply implement the
> > IQueryable extension methods and be done with that, that's doable, one
> > could oversee the consequences when something is changed, as it's not
> > that complex yet. it gets very complex very quickly after that.
> >
> > I.o.w.: if you don't have a design which says what you're doing
> > where, things get too complex to manage as it's not doable to dive in
> > and focus on something in particular, fix that and move on. Take group
> > join.
> > (join ... into.. ). It's simple at first, you simply pick one side
> > (left
> > side) and ignore the other. Till there's a DefaultIfEmpty. Then you
> > have to pull the OTHER side you ignored till then, at the spot of the
> > DefaultIfEmpty and change the join the DefaultIfEmpty is part of in a
> > left join.
> >
> > That's not 'some patch', that's a lot of work to get that right,
> > in all situations and it affects multiple stages in the
> > transformation, so design of the feature, then decisions where to make
> > the changes etc.
> > 'Creating a patch' is not going to work in this case (or in many other
> > cases with respect to the linq provider).
> >
> > I must say I'm a little surprised that apparently on this list
> > people think it is simply a matter of waiting for the right patch to
> > come along.
> >
> > FB
> >
> >