Documentation for leftOuterJoin expressions on MSDN are repeatedly implied through patterns that when using leftOuterJoin .. on .. into .. that you still have to use .DefaultIfEmpty() to achieve the desired effect.
I do not think this is necessary because I get the same results in both of these tests, which differ only in the fact that the second does not matter .DefaultIfEpmty()
type Test = A | B | C let G = [| A; B; C|] let H = [| A; C; C|] printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I.DefaultIfEmpty() do select (g, i)} printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I do select (g, i)} // seq [(A, A); (B, null); (C, C); (C, C)] // seq [(A, A); (B, null); (C, C); (C, C)]
1) Can you confirm this?
If this were correct, I only realized this after writing this alternative type increase in an attempt to better cope with unsurpassed results, and I was surprised to see null in my release!
type IEnumerable<'TSource> with member this.NoneIfEmpty = if (Seq.exists (fun _ -> true) this) then Seq.map (fun e -> Some e) this else seq [ None ] printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I.NoneIfEmpty do select (g, i)} // seq [(A, Some A); (B, Some null); (C, Some C); (C, Some C)]
2) Is there a way to get None instead of null / Some null from leftOuterJoin ?
3) What I really want to do is find out if there are any unsurpassed g
printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I.NoneIfEmpty do where (i.IsNone) exists (true) }
I realized that this is the following, but this is not very F #:
printfn "%A" <| query { for g in G do leftOuterJoin h in H on (g = h) into I for i in I do where (box i = null) exists (true)}