After the upgrade from .NET Core 3.1 to .NET 5.0, we have noticed a systematic error on all our queries that include NET Topology Suite but only within certain scenarios when there is a nested Select statement.
Those queries are now throwing 'Object reference not set to an instance of an object' exception.
Full example can be found here:
https://github.com/App-Impact-Labs/ISSUE-EFNetTopologySuiteProjectionQueryError
In the example above, we have three entities:
```C#
public class MedicalService
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<HealthInstitutionMedicalServiceLink> HealthInstitutionLinks { get; set; }
}
```C#
public class HealthInstitution
{
public int Id { get; set; }
public string Name { get; set; }
public Point Coordinates { get; set; }
public virtual IList<HealthInstitutionMedicalServiceLink> MedicalServiceLinks { get; set; }
}
```C#
public class HealthInstitutionMedicalServiceLink
{
public int Id { get; set; }
public int HealthInstitutionId { get; set; }
public virtual HealthInstitution HealthInstitution { get; set; }
public int MedicalServiceId { get; set; }
public virtual MedicalService MedicalService { get; set; }
}
1) This query works (coordinates are commented out):
```C#
HealthInstitutionGetDetailsRes dto = dbContext.HealthInstitutions
.Select(hi => new HealthInstitutionGetDetailsRes
{
Id = hi.Id,
Name = hi.Name,
//Coordinates = new HealthInstitutionGetDetailsRes.GeolocationDto
//{
// Latitude = hi.Coordinates.Y,
// Longitude = hi.Coordinates.X
//},
MedicalServiceIds = hi.MedicalServiceLinks.Select(msl => msl.MedicalServiceId)
})
.FirstOrDefault();
2) This one works also (ids are commented out):
```C#
HealthInstitutionGetDetailsRes dto = dbContext.HealthInstitutions
.Select(hi => new HealthInstitutionGetDetailsRes
{
Id = hi.Id,
Name = hi.Name,
Coordinates = new HealthInstitutionGetDetailsRes.GeolocationDto
{
Latitude = hi.Coordinates.Y,
Longitude = hi.Coordinates.X
},
//MedicalServiceIds = hi.MedicalServiceLinks.Select(msl => msl.MedicalServiceId)
})
.FirstOrDefault();
3) However full query with both coordinates and ids is not working (throws an exception):
```C#
HealthInstitutionGetDetailsRes dto = dbContext.HealthInstitutions
.Select(hi => new HealthInstitutionGetDetailsRes
{
Id = hi.Id,
Name = hi.Name,
Coordinates = new HealthInstitutionGetDetailsRes.GeolocationDto
{
Latitude = hi.Coordinates.Y,
Longitude = hi.Coordinates.X
},
MedicalServiceIds = hi.MedicalServiceLinks.Select(msl => msl.MedicalServiceId)
})
.FirstOrDefault();
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlFunctionExpression.GetHashCode()
at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
at Microsoft.EntityFrameworkCore.Query.SqlExpressions.SelectExpression.PushdownIntoSubquery()
at Microsoft.EntityFrameworkCore.Query.Internal.CollectionJoinApplyingExpressionVisitor.VisitExtension(Expression extensionExpression)
at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberAssignment(MemberAssignment node)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberBinding(MemberBinding node)
at System.Linq.Expressions.ExpressionVisitor.Visit[T](ReadOnlyCollection`1 nodes, Func`2 elementVisitor)
at System.Linq.Expressions.ExpressionVisitor.VisitMemberInit(MemberInitExpression node)
at System.Linq.Expressions.MemberInitExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.Internal.CollectionJoinApplyingExpressionVisitor.VisitExtension(Expression extensionExpression)
at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryTranslationPostprocessor.Process(Expression query)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
EF Core version: 5.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 5.0
Operating system: Windows 10 Enterprise
IDE: Visual Studio 2019 16.8.0
I verified this reproduces with 5.0 bits but works correctly on current main. Generated sql:
SELECT [t].[Id], [t].[Name], [t].[c], [t].[c0], [h0].[MedicalServiceId], [h0].[Id]
FROM (
SELECT TOP(1) [h].[Id], [h].[Name], [h].[Coordinates].Lat AS [c], [h].[Coordinates].Long AS [c0]
FROM [HealthInstitutions] AS [h]
) AS [t]
LEFT JOIN [HealthInstitutionMedicalServiceLinks] AS [h0] ON [t].[Id] = [h0].[HealthInstitutionId]
ORDER BY [t].[Id], [h0].[Id]
This https://github.com/dotnet/efcore/commit/43a5493251faf2591eb2f50abef4b8597374642f#diff-a307bbe9617539d71c4d3d72606c8b7328be03419f04e034b1e492977ca82aa3 fixed it. But we can extract out fix here for patch if needed. It is missing a null check in hash computation.
We have to do query splitting to fix our systems, so if it is possible to embed this for the next patch it would be appreciated.