diff --git a/src/EntityFrameworkCore.Projectables.Generator/SyntaxRewriters/ExpressionSyntaxRewriter.NullConditionalRewrite.cs b/src/EntityFrameworkCore.Projectables.Generator/SyntaxRewriters/ExpressionSyntaxRewriter.NullConditionalRewrite.cs
index 2810b4f..e662c29 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/SyntaxRewriters/ExpressionSyntaxRewriter.NullConditionalRewrite.cs
+++ b/src/EntityFrameworkCore.Projectables.Generator/SyntaxRewriters/ExpressionSyntaxRewriter.NullConditionalRewrite.cs
@@ -26,7 +26,29 @@ internal partial class ExpressionSyntaxRewriter
else if (_nullConditionalRewriteSupport is NullConditionalRewriteSupport.Ignore)
{
// Ignore the conditional access and simply visit the WhenNotNull expression
- return Visit(node.WhenNotNull);
+ var rewrittenExpression = (ExpressionSyntax?)Visit(node.WhenNotNull);
+ if (rewrittenExpression is null)
+ {
+ return null;
+ }
+
+ var typeInfo = _semanticModel.GetTypeInfo(node);
+ var whenNotNullType = _semanticModel.GetTypeInfo(node.WhenNotNull).Type;
+ if (IsCoalesceLeftOperand(node) &&
+ typeInfo.ConvertedType is INamedTypeSymbol { IsValueType: true, OriginalDefinition.SpecialType: SpecialType.System_Nullable_T } nullableType &&
+ whenNotNullType is { IsValueType: true } rewrittenType &&
+ (
+ SymbolEqualityComparer.Default.Equals(nullableType.TypeArguments[0], rewrittenType) ||
+ SymbolEqualityComparer.Default.Equals(nullableType, rewrittenType)
+ ))
+ {
+ return SyntaxFactory.CastExpression(
+ SyntaxFactory.ParseTypeName(nullableType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)),
+ SyntaxFactory.ParenthesizedExpression(rewrittenExpression)
+ );
+ }
+
+ return rewrittenExpression;
}
else if (_nullConditionalRewriteSupport is NullConditionalRewriteSupport.Rewrite)
@@ -105,4 +127,19 @@ private static bool IsNullableValueType(ITypeSymbol? type)
return type is { IsValueType: true } &&
type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T;
}
+
+ private static bool IsCoalesceLeftOperand(ConditionalAccessExpressionSyntax node)
+ {
+ ExpressionSyntax current = node;
+ var parent = node.Parent;
+
+ while (parent is ParenthesizedExpressionSyntax parenthesizedExpression)
+ {
+ current = parenthesizedExpression;
+ parent = parenthesizedExpression.Parent;
+ }
+
+ return parent is BinaryExpressionSyntax { RawKind: (int)SyntaxKind.CoalesceExpression } coalesceExpression &&
+ coalesceExpression.Left == current;
+ }
}
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/NullableTests.NullConditionalNullCoalesceTypeConversion_WithIgnoreSupport.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/NullableTests.NullConditionalNullCoalesceTypeConversion_WithIgnoreSupport.verified.txt
new file mode 100644
index 0000000..caa6ad7
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/NullableTests.NullConditionalNullCoalesceTypeConversion_WithIgnoreSupport.verified.txt
@@ -0,0 +1,15 @@
+//
+#nullable disable
+using EntityFrameworkCore.Projectables;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class _Product_GetCost
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Product @this) => (decimal? )(@this.Price.Amount) ?? 0m;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/NullableTests.cs b/tests/EntityFrameworkCore.Projectables.Generator.Tests/NullableTests.cs
index 00b601d..9d5af33 100644
--- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/NullableTests.cs
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/NullableTests.cs
@@ -488,6 +488,34 @@ class Foo {
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
+ [Fact]
+ public Task NullConditionalNullCoalesceTypeConversion_WithIgnoreSupport()
+ {
+ var compilation = CreateCompilation(@"
+using EntityFrameworkCore.Projectables;
+
+public class Price {
+ public decimal Amount { get; set; }
+}
+
+public class Product {
+ public Price? Price { get; set; }
+
+ [Projectable(AllowBlockBody = true, NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public decimal GetCost() {
+ return Price?.Amount ?? 0m;
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
[Fact]
public Task NullableValueType_MemberAccess_WithRewriteSupport_IsBeingRewritten()
{