Skip to content

feat: add QueryableProjectionNullHandling option to skip projection null handling#2369

Open
fdipuma wants to merge 7 commits into
riok:mainfrom
fdipuma:feat/2317-conversion-null-guard-strategy
Open

feat: add QueryableProjectionNullHandling option to skip projection null handling#2369
fdipuma wants to merge 7 commits into
riok:mainfrom
fdipuma:feat/2317-conversion-null-guard-strategy

Conversation

@fdipuma

@fdipuma fdipuma commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a QueryableProjectionNullHandling mapper option that lets IQueryable<T> projection mappings skip null handling, for relational providers (e.g. EF Core) where the projection lambda is translated to SQL and never executed in memory.

This implements part 3 of #2317 and should close:

Option

public enum QueryableProjectionNullHandling
{
    NullSafe = 0, // default, current null-safe behaviour
    Ignore,       // skip null handling in queryable projections
}

Set at mapper or assembly (MapperDefaults) level:

[Mapper(QueryableProjectionNullHandling = QueryableProjectionNullHandling.Ignore)]
public static partial class CarMapper
{
    public static partial IQueryable<CarDto> ProjectToDto(this IQueryable<Car> q);
}

Under Ignore, projection member access is emitted directly (x.Owner.Address.City, x.IntValue.Value for nullable value types) with no ?., ternary != null ? … : default, coalesce, or null fallback; the RMG089 nullability diagnostic is suppressed. Nullable value-type members are unwrapped so the generated expression compiles. The default NullSafe keeps today's behaviour unchanged (non-breaking).

note

The related idea from #2317 of honoring a conversion operator's own nullable annotations in non-projection mappings is intentionally not included here: it turned out to require changes to the shared member-mapping null-guard path and is best handled as a separate, focused PR. This PR is limited to the queryable-projection option.

Checklist

  • I did not use AI tools to generate this PR, or I have manually verified that the code is correct, optimal, and follows the project guidelines and architecture
  • I understand that low-quality, AI-generated PRs will be closed immediately without further explanation
  • The existing code style is followed
  • The commit message follows our guidelines
  • Performed a self-review of my code
  • Hard-to-understand areas of my code are commented
  • The documentation is updated (as applicable)
  • Unit tests are added/updated
  • Integration tests are added/updated (as applicable, especially if feature/bug depends on roslyn or framework version in use)

fdipuma added 7 commits July 13, 2026 22:20
…and add coverage

- NullMappedMemberSourceValue.Build: the Ignore short-circuit branch now
  requests addValuePropertyOnNullable so nullable value-type members
  (e.g. int? -> int) emit x.Member.Value instead of non-compiling x.Member.
- MemberMappingBuilder: extract the duplicated
  IsIgnoredProjectionNullHandling predicate used for diagnostic
  suppression and inline null-handling mapping.
- Add regression test for the value-type unwrap plus Verify coverage for
  a deeply nested nullable reference path and a nullable collection
  member under QueryableProjectionNullHandling.Ignore.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IQueryable projections have unnecessary null checks Unexpected IQueryable mapping when project's Nullable flag is set to "disable"

1 participant