Question

How to write this query in Linq to Entities:

SELECT * FROM TableA a LEFT JOIN TableB b ON a.Id = b.TableAId WHERE ISNULL(b.Id) OR b.FieldA = 1

Relationship is 1-to-many, but the restriction b.FIeldA = 1 is ensuring that actual data returned would be 1-0...1, which means that the number of returned records should be equal to records in TableA. I need to get all data from TableA, and joined data from TableB where exists. I am trying with this query, which actually preforms like a INNER JOIN (records from TableA that dont have related record in TableB are not retrieved)

Vladislav has suggested query liste below, to which I have added additional filtering in first query:

var query = (from x in myParentClasses.Include(x => x.TableBChildren)
         where !x.TableBChildren.Any(y => y.FieldA == 1)
         select x)
        .Concat(
         from x in myParentClasses.Include(x => x.TableBChildren)
         where x.TableBChildren.Any(y => y.FieldA == 1 || y.Id == null)
         select x)
        .ToList();

Generated sql looks like this:

SELECT   [Project1][...]
FROM     (SELECT [Extent1].[...],
                 [Extent2].[...],
                 [Extent3].[...],
                 CASE 
                   WHEN ([Extent3].[Id] IS NULL) THEN CAST(NULL AS int)
                   ELSE 1
                 END AS [C1]
          FROM   [dbo].[ParentTable] AS [Extent1]
                 LEFT OUTER JOIN [dbo].[ReferenceTable] AS [Extent2]
                   ON [Extent1].[ReferenceId] = [Extent2].[Id]
                 LEFT OUTER JOIN [dbo].[ChildrenTable] AS [Extent3]
                   ON [Extent1].[Id] = [Extent3].[ParentId]
          WHERE  [Extent1].[FieldA] = 1 /* @p__linq__0 */) AS [Project1]
ORDER BY [Project1].[Id] ASC,
         [Project1].[Id1] ASC,
         [Project1].[C1] ASC

Thanks, Goran

Was it helpful?

Solution

Linq-to-entities doesn't work exactly in the same way as SQL. It performs left join only if your query allows it (you don't have manual control over that behavior unless you manually write a join). It is not a case of your query because it uses x.TableBChildren.Any = it demands that parent table has any record from child table satisfying the condition thus it uses inner join.

I didn't try it and there is probably better way but I think this could work:

var query = (from x in myParentClasses.Include(x => x.TableBChildren)
             where !x.TableBChildren.Any()
             select x)
            .Concat(
             from x in myParentClasses.Include(x => x.TableBChildren)
             where x.TableBChildren.Any(y => y.FieldA == 1 || y.Id == null)
             select x)
            .ToList();

First part selects all records which doesn't have related entities and second part selects records with related entities satisfying your condition. It makes union of these two parts.

Also be aware that Include will not filter your relations - it will select all of them every time. If you want to filter them as well you must write the query with projection.

Edit:

If you want to have TableB filtered you cannot use normal query and Include. You also cannot return instances of MyParentClass. You must use projection:

var query = from x in myParentClasses
            select new 
              {
                  Id = x.Id,
                  // Rest of properties from myParentClass you want to receive
                  TableBChildren = x.TableBChildren.Where(y => y.FieldA == 1 || y.Id == null)
              };

This will return anonymous type with filtered TableBs. It is not possible to project into mapped entity (MyParentClass).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top