Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EF Core global query filter based on nullable property resulting in NullReferenceException #28940

Open
DaveSenn opened this issue Aug 31, 2022 · 9 comments

Comments

@DaveSenn
Copy link

Description

Queries using the below global filter result in a NullReferenceException.
It looks like EF always evaluates the second condition AllowedTenants.Contains( x.TenantId ) even if the first condition is true.

Include your code

public interface ITest
{
    IReadOnlyCollection<Int64>? GetTenants();
}

public sealed class Temp : IEfTenantProvider
{
    private readonly IArgosAuthorizationService _argosAuthorizationService;

    public IReadOnlyCollection<Int64>? GetTenants() =>
        null;
}

public class MyContextFactory : IDbContextFactory<MyContext>
{
    private readonly IDbContextFactory<MyContext> _pooledFactory;
    private readonly IReadOnlyCollection<Int64>? _allowedTenants;

    public MyContextFactory( IDbContextFactory<MyContext> pooledFactory, ITest tenant)
    {
        _pooledFactory = pooledFactory;
        _allowedTenants = tenant.GetTenants();
    }

    public MyContext CreateDbContext()
    {
        var context = _pooledFactory.CreateDbContext();
        context.AllowedTenants = _allowedTenants;
        return context;
    }
}

public class MyContext : DbContext
{
    public virtual DbSet<SomeTable> SomeTable { get; set; } = null!;

    public MyContext( DbContextOptions<MyContext> options )
        : base( options )
    {
    }

    public IReadOnlyCollection<Int64>? AllowedTenants { get; set; }
    protected override void OnModelCreating( ModelBuilder modelBuilder )
    {
        modelBuilder.Entity<SomeTable>().HasQueryFilter( x => AllowedTenants == null || AllowedTenants.Contains( x.TenantId ) );
    }
}

Include stack traces

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=Microsoft.EntityFrameworkCore.Relational
  StackTrace:
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.<VisitIn>g__ProcessInExpressionValues|29_0(SqlExpression valuesExpression, Boolean extractNullValues)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitIn(InExpression inExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean preserveColumnNullabilityInformation, Boolean& nullable)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean preserveColumnNullabilityInformation, Boolean& nullable)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SelectExpression selectExpression)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(TableExpressionBase tableExpressionBase)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(TableExpressionBase tableExpressionBase)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SelectExpression selectExpression)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(TableExpressionBase tableExpressionBase)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(TableExpressionBase tableExpressionBase)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SelectExpression selectExpression)
   at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Process(SelectExpression selectExpression, IReadOnlyDictionary`2 parameterValues, Boolean& canCache)
   at Microsoft.EntityFrameworkCore.Query.RelationalParameterBasedSqlProcessor.Optimize(SelectExpression selectExpression, IReadOnlyDictionary`2 parametersValues, Boolean& canCache)
   at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerParameterBasedSqlProcessor.Optimize(SelectExpression selectExpression, IReadOnlyDictionary`2 parametersValues, Boolean& canCache)
   at Microsoft.EntityFrameworkCore.Query.Internal.RelationalCommandCache.GetRelationalCommandTemplate(IReadOnlyDictionary`2 parameters)
   at Microsoft.EntityFrameworkCore.Internal.RelationCommandCacheExtensions.RentAndPopulateRelationalCommand(RelationalCommandCache relationalCommandCache, RelationalQueryContext queryContext)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.<<PopulateSplitIncludeCollectionAsync>g__InitializeReaderAsync|63_0>d`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass33_0`2.<<ExecuteAsync>b__0>d.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<ExecuteImplementationAsync>d__34`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<ExecuteImplementationAsync>d__34`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<ExecuteAsync>d__33`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.<PopulateSplitIncludeCollectionAsync>d__63`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.<TaskAwaiter>d__69.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable`1.AsyncEnumerator.<MoveNextAsync>d__19.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleAsync>d__14`1.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleAsync>d__14`1.MoveNext()

Include provider and version information

EF Core 6.0.8
SQL Server 2019

Microsoft Visual Studio Professional 2022
Version 17.3.2
VisualStudio.17.Release/17.3.2+32819.101
Microsoft .NET Framework
Version 4.8.04084

Installed Version: Professional

Visual C++ 2022 00476-80000-00000-AA450
Microsoft Visual C++ 2022

ASP.NET and Web Tools 17.3.376.3011
ASP.NET and Web Tools

Azure App Service Tools v3.0.0 17.3.376.3011
Azure App Service Tools v3.0.0

Azure Functions and Web Jobs Tools 17.3.376.3011
Azure Functions and Web Jobs Tools

C# Tools 4.3.0-3.22412.4+c97184bafab9a34d61e85f1c1ef34f25283ce9ba
C# components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.

Common Azure Tools 1.10
Provides common services for use by Azure Mobile Services and Microsoft Azure Tools.

Entity Framework Core Power Tools 2.5
Adds useful design-time EF Core DbContext features to the Visual Studio Solution Explorer context menu.

Microsoft JVM Debugger 1.0
Provides support for connecting the Visual Studio debugger to JDWP compatible Java Virtual Machines

Node.js Tools 1.5.40629.1 Commit Hash:3f5cc0329815af3ffb948f08857446d206a9af36
Adds support for developing and debugging Node.js apps in Visual Studio

NuGet Package Manager 6.3.0
NuGet Package Manager in Visual Studio. For more information about NuGet, visit https://docs.nuget.org/

Razor (ASP.NET Core) 17.0.0.2232702+e1d654e792aa2fe6646a6935bcca80ff0aff4387
Provides languages services for ASP.NET Core Razor.

SQL Server Data Tools 17.0.62207.04100
Microsoft SQL Server Data Tools

Switch Startup Project 4.2.76
Provides a toolbar dropdown box to switch between startup projects.

Test Adapter for Boost.Test 1.0
Enables Visual Studio's testing tools with unit tests written for Boost.Test. The use terms and Third Party Notices are available in the extension installation directory.

Test Adapter for Google Test 1.0
Enables Visual Studio's testing tools with unit tests written for Google Test. The use terms and Third Party Notices are available in the extension installation directory.

TypeScript Tools 17.0.10701.2001
TypeScript Tools for Microsoft Visual Studio

Visual Basic Tools 4.3.0-3.22412.4+c97184bafab9a34d61e85f1c1ef34f25283ce9ba
Visual Basic components used in the IDE. Depending on your project type and settings, a different version of the compiler may be used.

Visual F# Tools 17.1.0-beta.22363.4+1b94f89d4d1f41f20f9be73c76f4b229d4e49078
Microsoft Visual F# Tools

Visual Studio IntelliCode 2.2
AI-assisted development for Visual Studio.

VsChromium 0.9.39
Collection of tools to help contributing code to the Chromium project.

VSColorOutput64 2022.2
Color output for build and debug windows - https://mike-ward.net/vscoloroutput

@davidkeaveny
Copy link

davidkeaveny commented Sep 21, 2022

@DaveSenn are you finding that EF logs the generated SQL before it executes? I'm getting the same outcome with a slightly different scenario - while I do have a global filter, it's not on a nullable field; I'm applying a more complex operation with the EF.Property<TProperty>(object, string) helper, like this:

switch (employerNumber)
{
    case AustralianBusinessNumber abn:
        ApplyPredicate(client => 
            EF.Property<string>(client, "Identifier") == abn.Value && 
            (EF.Property<string?>(client, "BranchNumber") == abn.BranchNumber || EF.Property<string?>(client, "BranchNumber") == null));
        break;
    case WithholdingPayerNumber wpn:
        ApplyPredicate(client => 
            EF.Property<string>(client, "Identifier") == wpn.Value);
        break;
}

BranchNumber is the only nullable column on my table (and the only nullable field on my entity model), but I can't see the SQL being generated since it only happens in production - that is, I've not been able to reproduce it yet locally.

I am currently using EF Core 6.0.3.

@excelkobayashi

This comment was marked as off-topic.

@roji

This comment was marked as off-topic.

@excelkobayashi

This comment was marked as off-topic.

@roji

This comment was marked as off-topic.

@excelkobayashi

This comment was marked as off-topic.

@excelkobayashi

This comment was marked as off-topic.

@roji

This comment was marked as off-topic.

@roji roji self-assigned this Jan 10, 2023
@roji roji removed this from the Backlog milestone Jan 10, 2023
@roji roji removed their assignment Jan 10, 2023
@roji roji added this to the Backlog milestone Jan 10, 2023
@roji

This comment was marked as off-topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants