Frans, I think we're hijacking thist thread for discussions about re-linq; therefore, I'm moving my answer to the re-motion users list: http://groups.google.com/group/re-motion-users/t/3062c49267adfa5f .
Cheers, Fabian On Sep 5, 11:49 am, "Frans Bouma" <[email protected]> wrote: > > We try to keep the re-linq front-end as general as possible. The fact that > > DefaultIfEmpty causes a GroupJoin to turn into a left-outer join in your > > example is true, but it's not as simple generally speaking. > > Consider the following slightly modified version of your query: > > > var q = from c in ctxt.Order > > join o in ctxt.Order on c.CustomerId equals o.Customer.CustomerId > > into co // A > > from x in co.DefaultIfEmpty() //B > > from y in co //C > > select c; > > > Here, B uses the group join in a left-outer join fashion, but C uses it in > > an ordinary cross join fashion. > > Is that really the case? 'y' is an order, same as 'x', but 'co' is > the same set as when the DefaultIfEmpty() call is made. > > I do understand why you're saying it, from the perspective of > sequences, it makes sense, however from the perspective of sets and SQL, I > don't really know how to formulate the query above, because one has to join > co again with... co ? (which can't be done). > > At the moment I follow the same steps, but it's a cumbersome path > IMHO, where if you look at 'intent', it can be made simpler, IMHO. But I > understand why adding this to the front end might make situations like the > one above harder to deal with (although I'm interested in what you'd think > the query above does, how it looks in SQL :)) > > > > > The simplification you suggested (keeping A > > and flagging it as a left join) won't work here. And my opinion is that if > > we can't apply it generally, we shouldn't apply it at all. (As a > predefined > > transformation in the re-linq front-end, that is. Any LINQ provider can of > > course choose to build this simplification if it helps.) > > IMHO the sole purpose of 'DefaultIfEmpty' is to use a sequence which > can contain nulls due to a left join, and in fact, it's the only mechanism > to force a left join into the final query (navigator traversal might result > in left joins due to nullable FK fields, but that's not enforceable) using a > 'join' operator or a navigator traversal which would otherwise result in an > inner join. > > I.o.w.: if you see a DefaultIfEmpty, the sequence it is called on is > a result of a left join, and you can treat that sequence as such. Dealing > with it 'on the fly' otherwise is a very cumbersome task as it's 'too late': > either group joins are already handled in some trees or other clauses refer > to the defaultifempty sequence and do that by its own alias (and not the > alias of the side the defaultifempty refers to) > > > > > In addition, as Stefan said, DefaultIfEmpty can be applied to any > arbitrary > > sequence in a query, not only group joins. A LINQ provider striving to > > support DefaultIfEmpty should try to find a solution that works in all > > cases, no matter where the DefaultIfEmpty operator is applied. > > yes, that's true. The second query I gave is indeed an example of > that. Though is it really a matter of 'a lot of cases' btw? Aren't there > just 2: 1) a group join result used with DefaultIfEMpty -> make groupjoin a > left join and 2) the random sequence on which DefaultIfEmpty() is called, > which also would result in a left join? (x=> c.Orders.DefaultIfEmpty()) > > The part I'm struggling with is the query you gave above, which to > me is unspecifyable in SQL, or at least I don't really see the 'intent' of > what such a construct might be. Could you elaborate on that a bit please? > > > > > I've written a blog post about how I'd handle the DefaultIfEmpty query > > operator in a SQL-translating LINQ provider here: > >https://www.re-motion.org/blogs/mix/archive/2010/09/04/handling-the- > > defaultifempty-result-operator-in-a-re-linq-based-provider.aspx > > . This describes more or less how we deal with DefaultIfEmpty in re- > linq's > > SQL backend, and I think you'll agree that with an approach such as the > one > > explained in the blog post, DefaultIfEmpty isn't any longer difficult to > > implement. > > .. except that you have a tremendous amount of work ahead of you: > optimize the query you're producing :). This is rather difficult to do > though, Linq to SQL (the best linq provider out there still) uses a lot of > visitors for this, which can take significant amount of time during query > production. > > I do like the approach though, it makes things easier on the > expression tree front, at the expense of optimizing it later (something the > EF linq provider also does) with the assumption that optimizing queries is > easier. It's at least more straight forward! ;) > > What I learned from the demoscene so many years ago though, was that > if you can do things up front, it will save you a lot of time later on. > I.o.w.: if you run into a defaultifempty, you can also think: why is it > there? My point is then: what if the answer to that question is: "to make a > join a left join"? This info is then usable to rework the tree a bit to > reflect that info so the expression tree handling is easier and you don't > need optimization afterwards. The question of course is: is it indeed only > there for that purpose or not? (in the context of database targeting > queries, as not all IQueryable operators make sense in every location of > such a query when you see them in the context of db targeting queries). > > I have no clear answer to that. THe query you gave above as an > example clearly proves me wrong, so we can look into other ways perhaps to > deal with this, but on the other hand, the query construct also might not > make much sense in the context of a db query, which may mitigate the > argument. IF we can find evidence DefaultIfEmpty is there for making joins > left joins, things will get much easier to implement IMHO (including group > join). > > Disclaimer: I haven't looked at these constructs for quite a while, > (read: more 1.5 years) so I might overlook an important aspect. > > FB > > > > > > > Cheers, > > Fabian > > > On Sep 4, 12:02 pm, "Frans Bouma" <[email protected]> wrote: > > > > > from timeHeader in metaData.NonPresentTimeHeader where > > > > > timeHeader.Id == 6 join time in metaData.NonPresentTime on > > > > > timeHeader.Id equals time.HeaderId into timeJoin from joinedTime > > > > > in > > > > > timeJoin.DefaultIfEmpty() select joinedTime > > > > > > What might be a solution is to add a 'jointype' to the groupjoin > > > > > class of re-linq and switch it to 'left join' when you handle the > > > > > p=>DefaultIfEmpty() construct of a re-linq tree. > > > > > Hi Frans, > > > > > we could transform simple left-join-via-DefaultIfEmpty to left join > > > clauses > > > > in re-linq's QueryModel, but that would be a very simplistic > > > > solution that could not support any but the most straightforward LINQ > > clauses. (i.e. > > > only > > > > those where someone implements left join as they found out via > > > > Google, but for instance not any scenario where the query further > > > > references the intermediate join clause (timeJoin in your sample). > > > > A simple, stupid, but illustrative example: ('ctxt' is > > > session.Linq... ) var q = from c in ctxt.Order > > > join o in ctxt.Order on c.CustomerId equals > > > o.Customer.CustomerId Into co // A > > > from x in co.DefaultIfEmpty() //B > > > select c; > > > > Lines A and B form a SelectMany, due to the from in B. At the left > > > side, you'll have the group join in A and on the right side you have > > > the right side of the group join in A. > > > > This can be transformed into keeping A, and using B to adjust the > > > group join. The main advantage is that the group join contains the > > > selectors and in C#'s case also the projection. > > > > So if re-linq could transform this into (pseudo code!) var q = from c > > > in ctxt.Order > > > (left) join o in ctxt.Order on c.CustomerId equals > > > o.Customer.CustomerId Into co select c; > > > > it would be a big win. Big problem is the code which references the > > > DefaultIfEmpty result as that has to be changed to references to the > > > right-side of the join. > > > > It would help a lot, because it would make a join handler in a linq > > > provider much easier, in fact, an existing join handler (which can > > > handle the normal, > > > 'join') would likely already work. > > > > > We think that DefaultIfEmpty is best solved in each back-end > > > > individually, but of course nothing stops anyone from extending the > > > > front-end to create that kind of QueryModel via an optional > > > > transformation step. Just be aware that this only takes you so far, > > > > it would probably result in the LINQ provider rejecting every use of > > > > DefaultIfEmtpy in joins that cannot be reduced to that pattern. > > > > You mean: > > > var q = from c in ctxt.Order > > > from o in c.Orders.DefaultIfEmpty() > > > select c; > > > > ? > > > > FB- Hide quoted text - > > > > - Show quoted text-- Hide quoted text - > > - Show quoted text -
