Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform a group join in .NET Core 3.0 Entity Framework?

Tags:

With the changes to .NET Core 3.0 I am getting

... NavigationExpandingExpressionVisitor' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.) ---> System.InvalidOperationException: Processing of the LINQ expression 'GroupJoin, ...

This is a really simple query so there must be a way to perform it in .NET CORE 3.0:

 var queryResults1 = await patients             .GroupJoin(                 _context.Studies,                 p => p.Id,                 s => s.Patient.Id,                 (p, studies) => new                  {                     p.DateOfBirth,                     p.Id,                     p.Name,                     p.Sex,                    Studies =studies.Select(s1=>s1)                 }             )             .AsNoTracking().ToListAsync(); 

I am basically looking for a Linq query (or method syntax as above) which will join Studies onto Patients, and set Studies to an empty list or null if there are no studies for the given patient.

Any ideas? This was working in .NET Core 2.2. Also the MSFT link above mentions that the key breaking change is related to client side evaluation and avoiding that the generated query reads entire tables which must then be joined or filtered client side. However with this simple query, the join should be easily doable server side.

like image 768
shelbypereira Avatar asked Oct 30 '19 09:10

shelbypereira


People also ask

How do I join multiple tables in Entity Framework?

The LINQ join operator allows us to join multiple tables on one or more columns (multiple columns). By default, they perform the inner join of the tables. We also learn how to perform left joins in Entity Framework by using the join operator & DefaultIfEmpty method.

How do I use joins in Entity Framework?

Entity Framework Joining In SQL, a JOIN clause is used to combine data from two or more tables, based on a related column between them. Similarly, in Entity Framework, the LINQ Join is used to load data from two or more tables.

What is group join in Linq?

The group join is useful for producing hierarchical data structures. It pairs each element from the first collection with a set of correlated elements from the second collection. For example, a class or a relational database table named Student might contain two fields: Id and Name .


Video Answer


2 Answers

As discussed here, you're attempting a query that isn't supported by the database. EF Core 2 used client-side evaluation to make your code work, but EF Core 3 refuses, because the client-side convenience comes at the cost of hard-to-debug performance problems as the dataset increases.

You can use use DefaultIfEmpty to left join the patients' studies and then group manually with ToLookup.

var query =     from p in db.Patients     join s in db.Studies on p.Id equals s.PatientId into studies     from s in studies.DefaultIfEmpty()     select new { Patient = p, Study = s };  var grouping = query.ToLookup(e => e.Patient); // Grouping done client side 

The above example grabs the full Patient and Study entities, but you can cherry pick columns instead. If the data you need from Patient is too big to repeat for each Study, in the joined query select only the Patient ID, querying the rest of the Patient data in a separate non-joined query.

like image 191
Edward Brey Avatar answered Sep 24 '22 10:09

Edward Brey


Had exactly the same issue and a big struggle with it. It turns out that .net Core 3.0 does not support Join or Groupjoin in method syntax (yet?). The fun part is though, it does work in Query syntax.

Try this, it's query syntax with a bit of method syntax. This translates nicely to the correct SQL query with a nice left outer join and it is processed on the database. I haven't got your models so you need to check the syntax yourselves....

var queryResults1 =      (from p in _context.patients     from s in _context.Studies.Where(st => st.PatientId == p.Id).DefaultIfEmpty()     select new     {         p.DateOfBirth,         p.Id,         p.Name,         p.Sex,         Studies = studies.Select(s1 => s1)     }).ToListAsync(); 
like image 45
hwmaat Avatar answered Sep 23 '22 10:09

hwmaat