dotnet / dotnet/efcore

EF 7.0.9 returns a non-null value despite explicitly selecting for a nullable value.

Open
#31,260 4 comments 0 reactions 0 assignees View on GitHub
area-query customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

EF 7.0.9 returns a non-null value despite explicitly selecting for a nullable value.

Seems similar to https://github.com/dotnet/efcore/issues/26744

Here is a complete Console app repro:

### `Program.cs`
```C#
using Microsoft.EntityFrameworkCore;

// Reset DB
var db = new TestContext();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();

// Add Customers
{
// #1 - customer with active plan
db.Add(new Customer { Id = 1, ServicePlan = new ServicePlan { TerminationDate = null } });

// #2 - customer with past plan
db.Add(new Customer { Id = 2, ServicePlan = new ServicePlan { TerminationDate = DateTime.Now.AddDays(-1) } });

// #3 - customer who never had a plan
db.Add(new Customer { Id = 3, ServicePlan = null });

db.SaveChanges();
}

// Test queries

// OK
var neverCustomers = db.Customers.Where(c => c.ServicePlan == null).ToList();
PrintCustomerIds(neverCustomers); // "3"

// OK
var customers = db.Customers.Where(c => c.ServicePlan != null).ToList();
PrintCustomerIds(customers); // "1, 2"

// OK
var pastCustomers = db.Customers.Where(c => c.ServicePlan.TerminationDate != null).ToList();
PrintCustomerIds(pastCustomers); // "2"

// OK
var validData = db.Customers
.Select(c => c.ServicePlan == null ? (bool?)null : c.ServicePlan.TerminationDate != null);
PrintNullableBools(validData); // OUTPUT: "False, True, Null"

// BUG. Should produce the same output as the previous query.
var badData = db.Customers
.Select(c => c.ServicePlan == null ? (bool?)null : c.ServicePlan.TerminationDate.HasValue);
PrintNullableBools(badData); // OUTPUT: "False, True, False". EXPECTED: "False, True, Null"

// Helpers
void PrintCustomerIds(IEnumerable customers)
=> Console.WriteLine(string.Join(", ", customers.Select(x => x.Id)));

void PrintNullableBools(IEnumerable data)
=> Console.WriteLine(string.Join(", ",
data.Select(b => b == null ? "Null" : b.ToString())));

// Model
public class Customer
{
public int Id { get; set; }
public ServicePlan? ServicePlan { get; set; }
}

public class ServicePlan
{
public int Id { get; set; }
public DateTime? TerminationDate { get; set; }
}

// DbContext
public class TestContext : DbContext
{
public DbSet Customers { get; set; }
public DbSet ServicePlans { get; set; }

public string DbPath { get; }

public TestContext()
{
var folder = Environment.SpecialFolder.LocalApplicationData;
var path = Environment.GetFolderPath(folder);
DbPath = Path.Join(path, "EfCoreNullableQuerying.db");
}

protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite($"Data Source={DbPath}");
}
```

### `EfCoreNullableQuerying.csproj`
```


Exe
net7.0
enable
enable



```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.