diff --git a/README.md b/README.md
index ab688bf..471c6f5 100644
--- a/README.md
+++ b/README.md
@@ -159,6 +159,39 @@ GROUP BY (COALESCE("u"."FirstName", '') || ' ') || COALESCE("u"."LastName", '')
ORDER BY (COALESCE("u"."FirstName", '') || ' ') || COALESCE("u"."LastName", '')
```
+#### Can I use block-bodied members instead of expression-bodied members?
+
+Yes! As of version 6.x, you can now use traditional block-bodied members with `[Projectable]`. This makes code more readable when dealing with complex conditional logic:
+
+```csharp
+// Expression-bodied (still supported)
+[Projectable]
+public string Level() => Value > 100 ? "High" : Value > 50 ? "Medium" : "Low";
+
+// Block-bodied (now also supported!)
+[Projectable(AllowBlockBody = true)] // Note: AllowBlockBody is required to remove the warning for experimental feature usage
+public string Level()
+{
+ if (Value > 100)
+ return "High";
+ else if (Value > 50)
+ return "Medium";
+ else
+ return "Low";
+}
+```
+
+> This is an experimental feature and may have some limitations. Please refer to the documentation for details.
+
+Both generate identical SQL. Block-bodied members support:
+- If-else statements (converted to ternary/CASE expressions)
+- Switch statements
+- Local variables (automatically inlined)
+- Simple return statements
+
+The generator will also detect and report side effects (assignments, method calls to non-projectable members, etc.) with precise error messages. See [Block-Bodied Members Documentation](docs/BlockBodiedMembers.md) for complete details.
+
+
#### How do I expand enum extension methods?
When you have an enum property and want to call an extension method on it (like getting a display name from a `[Display]` attribute), you can use the `ExpandEnumMethods` property on the `[Projectable]` attribute. This will expand the enum method call into a chain of ternary expressions for each enum value, allowing EF Core to translate it to SQL CASE expressions.
diff --git a/docs/BlockBodiedMembers.md b/docs/BlockBodiedMembers.md
new file mode 100644
index 0000000..63be89f
--- /dev/null
+++ b/docs/BlockBodiedMembers.md
@@ -0,0 +1,410 @@
+# Block-Bodied Methods Support
+
+EntityFrameworkCore.Projectables now supports "classic" block-bodied members (methods and properties) decorated with `[Projectable]`, in addition to expression-bodied members.
+
+## ⚠️ Experimental Feature
+
+Block-bodied members support is currently **experimental**. By default, using a block-bodied member with `[Projectable]` will emit a warning:
+
+```
+EFP0001: Block-bodied member 'MethodName' is using an experimental feature. Set AllowBlockBody = true on the Projectable attribute to suppress this warning.
+```
+
+To acknowledge that you're using an experimental feature and suppress the warning, set `AllowBlockBody = true`:
+
+```csharp
+[Projectable(AllowBlockBody = true)]
+public string GetCategory()
+{
+ if (Value > 100)
+ {
+ return "High";
+ }
+ else
+ {
+ return "Low";
+ }
+}
+```
+
+This requirement will be removed in a future version once the feature is considered stable.
+
+## What's Supported
+
+Block-bodied methods can now be transformed into expression trees when they contain:
+
+### 1. Simple Return Statements
+```csharp
+[Projectable]
+public int GetConstant()
+{
+ return 42;
+}
+```
+
+### 2. If-Else Statements (converted to ternary expressions)
+```csharp
+[Projectable]
+public string GetCategory()
+{
+ if (Value > 100)
+ {
+ return "High";
+ }
+ else
+ {
+ return "Low";
+ }
+}
+```
+
+### 3. Nested If-Else Statements
+```csharp
+[Projectable]
+public string GetLevel()
+{
+ if (Value > 100)
+ {
+ return "High";
+ }
+ else if (Value > 50)
+ {
+ return "Medium";
+ }
+ else
+ {
+ return "Low";
+ }
+}
+```
+
+### 4. Local Variable Declarations (inlined into the expression)
+```csharp
+[Projectable]
+public int CalculateDouble()
+{
+ var doubled = Value * 2;
+ return doubled + 5;
+}
+
+// Transitive inlining is also supported:
+[Projectable]
+public int CalculateComplex()
+{
+ var a = Value * 2;
+ var b = a + 5;
+ return b + 10; // Fully expanded to: Value * 2 + 5 + 10
+}
+```
+
+**⚠️ Important Notes:**
+- Local variables are inlined at each usage point, which duplicates the initializer expression
+- If a local variable is used multiple times, its initializer expression is duplicated at each usage, which can change semantics if the initializer has side effects
+- Local variables can only be declared at the method body level, not inside nested blocks (if/switch/etc.)
+- Variables are fully expanded transitively (variables that reference other variables are fully inlined)
+
+### 5. Switch Statements (converted to nested ternary expressions)
+```csharp
+[Projectable]
+public string GetValueLabel()
+{
+ switch (Value)
+ {
+ case 1:
+ return "One";
+ case 2:
+ return "Two";
+ case 3:
+ return "Three";
+ default:
+ return "Many";
+ }
+}
+```
+
+### 6. If Statements Without Else (uses default value)
+```csharp
+// Pattern 1: Explicit null return
+[Projectable]
+public int? GetPremiumIfActive()
+{
+ if (IsActive)
+ {
+ return Value * 2;
+ }
+ return null; // Explicit return for all code paths
+}
+
+// Pattern 2: Explicit fallback return
+[Projectable]
+public string GetStatus()
+{
+ if (IsActive)
+ {
+ return "Active";
+ }
+ return "Inactive"; // Explicit fallback
+}
+```
+
+### 7. Multiple Early Returns (converted to nested ternary expressions)
+```csharp
+[Projectable]
+public string GetValueCategory()
+{
+ if (Value > 100)
+ {
+ return "Very High";
+ }
+
+ if (Value > 50)
+ {
+ return "High";
+ }
+
+ if (Value > 10)
+ {
+ return "Medium";
+ }
+
+ return "Low";
+}
+
+// Converted to: Value > 100 ? "Very High" : (Value > 50 ? "High" : (Value > 10 ? "Medium" : "Low"))
+```
+
+## Limitations and Warnings
+
+The source generator will produce **warning EFP0003** when it encounters unsupported statements in block-bodied methods:
+
+### Unsupported Statements:
+- While, for, foreach loops
+- Try-catch-finally blocks
+- Throw statements
+- New object instantiation in statement position
+
+### Example of Unsupported Pattern:
+```csharp
+[Projectable]
+public int GetValue()
+{
+ for (int i = 0; i < 10; i++) // ❌ Loops not supported
+ {
+ // ...
+ }
+ return 0;
+}
+```
+
+Supported patterns:
+```csharp
+[Projectable]
+public int GetValue()
+{
+ if (IsActive) // ✅ If without else is now supported!
+ {
+ return Value;
+ }
+ else
+ {
+ return 0;
+ }
+}
+```
+
+Additional supported patterns:
+```csharp
+// If without else using fallback return:
+[Projectable]
+public int GetValue()
+{
+ if (IsActive)
+ {
+ return Value;
+ }
+ return 0; // ✅ Fallback return
+}
+
+// Switch statement:
+[Projectable]
+public string GetLabel()
+{
+ switch (Value) // ✅ Switch statements now supported!
+ {
+ case 1:
+ return "One";
+ case 2:
+ return "Two";
+ default:
+ return "Other";
+ }
+}
+```
+
+Or as expression-bodied:
+```csharp
+[Projectable]
+public int GetValue() => IsActive ? Value : 0; // ✅ Expression-bodied
+```
+
+## How It Works
+
+The source generator:
+1. Parses block-bodied methods
+2. Converts if-else statements to conditional (ternary) expressions
+3. Converts switch statements to nested conditional expressions
+4. Inlines local variables into the return expression
+5. Rewrites the resulting expression using the existing expression transformation pipeline
+6. Generates the same output as expression-bodied methods
+
+## Benefits
+
+- **More readable code**: Complex logic with nested conditions and switch statements is often easier to read than nested ternary operators
+- **Gradual migration**: Existing code with block bodies can now be marked as `[Projectable]` without rewriting
+- **Intermediate variables**: Local variables can make complex calculations more understandable
+- **Switch support**: Traditional switch statements now work alongside switch expressions
+
+## SQL Output Examples
+
+### Switch Statement with Multiple Cases
+Given this code:
+```csharp
+switch (Value)
+{
+ case 1:
+ case 2:
+ return "Low";
+ case 3:
+ case 4:
+ case 5:
+ return "Medium";
+ default:
+ return "High";
+}
+```
+
+Generates optimized SQL:
+```sql
+SELECT CASE
+ WHEN [e].[Value] IN (1, 2) THEN N'Low'
+ WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium'
+ ELSE N'High'
+END
+FROM [Entity] AS [e]
+```
+
+### If-Else Example Output
+
+Given this code:
+```csharp
+public record Entity
+{
+ public int Value { get; set; }
+ public bool IsActive { get; set; }
+
+ [Projectable]
+ public int GetAdjustedValue()
+ {
+ if (IsActive && Value > 0)
+ {
+ return Value * 2;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+```
+
+The generated SQL will be:
+```sql
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0
+ THEN [e].[Value] * 2
+ ELSE 0
+END
+FROM [Entity] AS [e]
+```
+
+## Side Effect Detection
+
+The generator provides specific error reporting for side effects in block-bodied methods, helping you identify and fix issues quickly.
+
+### Detected Side Effects
+
+#### 1. Property Assignments (EFP0004 - Error)
+
+Property assignments modify state and are not allowed:
+
+```csharp
+[Projectable]
+public int Foo()
+{
+ Bar = 10; // ❌ Error: Assignment operation has side effects
+ return Bar;
+}
+```
+
+#### 2. Compound Assignments (EFP0004 - Error)
+
+Compound assignment operators like `+=`, `-=`, `*=`, etc. are not allowed:
+
+```csharp
+[Projectable]
+public int Foo()
+{
+ Bar += 10; // ❌ Error: Compound assignment operator '+=' has side effects
+ return Bar;
+}
+```
+
+#### 3. Increment/Decrement Operators (EFP0004 - Error)
+
+Pre and post increment/decrement operators are not allowed:
+
+```csharp
+[Projectable]
+public int Foo()
+{
+ var x = 5;
+ x++; // ❌ Error: Increment/decrement operator '++' has side effects
+ return x;
+}
+```
+
+#### 4. Non-Projectable Method Calls (EFP0005 - Warning)
+
+Calls to methods not marked with `[Projectable]` may have side effects:
+
+```csharp
+[Projectable]
+public int Foo()
+{
+ Console.WriteLine("test"); // ⚠️ Warning: Method call 'WriteLine' may have side effects
+ return Bar;
+}
+```
+
+### Diagnostic Codes
+
+- **EFP0003**: Unsupported statement in block-bodied method (Warning)
+- **EFP0004**: Statement with side effects in block-bodied method (Error)
+- **EFP0005**: Potential side effect in block-bodied method (Warning)
+
+### Error Message Improvements
+
+Instead of generic error messages, you now get precise, actionable feedback:
+
+**Before:**
+```
+warning EFP0003: Method 'Foo' contains an unsupported statement: Expression statements are not supported
+```
+
+**After:**
+```
+error EFP0004: Property assignment 'Bar' has side effects and cannot be used in projectable methods
+```
+
+The error message points to the exact line with the problematic code, making it much easier to identify and fix issues.
+
diff --git a/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs b/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs
index 30af683..94b63b2 100644
--- a/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs
+++ b/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs
@@ -38,5 +38,14 @@ public sealed class ProjectableAttribute : Attribute
///
///
public bool ExpandEnumMethods { get; set; }
+
+ ///
+ /// Get or set whether to allow block-bodied members (experimental feature).
+ ///
+ ///
+ /// Block-bodied method support is experimental and may have limitations.
+ /// Set this to true to suppress the experimental feature warning.
+ ///
+ public bool AllowBlockBody { get; set; }
}
}
diff --git a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Shipped.md b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Shipped.md
index 586c754..253db78 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Shipped.md
+++ b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Shipped.md
@@ -1,7 +1,25 @@
-## Release 5.0
+## Release 6.0
### New Rules
Rule ID | Category | Severity | Notes
---------|----------|----------|--------------------
-EFP0001 | Design | Error |
+--------|----------|----------|-------------------------------------------------------------------------
+EFP0003 | Design | Warning | Unsupported statement in block-bodied method
+EFP0004 | Design | Error | Statement with side effects in block-bodied method
+EFP0005 | Design | Warning | Potential side effect in block-bodied method
+EFP0006 | Design | Error | Method or property should expose a body definition (block or expression)
+
+### Changed Rules
+
+Rule ID | New Category | New Severity | Old Category | Old Severity | Notes
+--------|--------------|--------------|--------------|--------------|-----------------------------------------------------------------
+EFP0001 | Design | Warning | Design | Error | Changed to warning for experimental block-bodied members support
+
+## Release 5.0
+
+### New Rules
+
+Rule ID | Category | Severity | Notes
+--------|----------|----------|------------------------------------------------------------------------------
+EFP0001 | Design | Error | Method or property should expose an expression body definition
+EFP0002 | Design | Error | Method or property is not configured to support null-conditional expressions
diff --git a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md
index ef168b8..5f28270 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md
+++ b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md
@@ -1,5 +1 @@
-### New Rules
-
-Rule ID | Category | Severity | Notes
---------|----------|----------|--------------------
-EFP0002 | Design | Error |
+
\ No newline at end of file
diff --git a/src/EntityFrameworkCore.Projectables.Generator/BlockStatementConverter.cs b/src/EntityFrameworkCore.Projectables.Generator/BlockStatementConverter.cs
new file mode 100644
index 0000000..16f7fae
--- /dev/null
+++ b/src/EntityFrameworkCore.Projectables.Generator/BlockStatementConverter.cs
@@ -0,0 +1,590 @@
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace EntityFrameworkCore.Projectables.Generator
+{
+ ///
+ /// Converts block-bodied methods to expression syntax that can be used in expression trees.
+ /// Only supports a subset of C# statements.
+ ///
+ public class BlockStatementConverter
+ {
+ private readonly SourceProductionContext _context;
+ private readonly ExpressionSyntaxRewriter _expressionRewriter;
+ private readonly Dictionary _localVariables = new();
+
+ public BlockStatementConverter(SourceProductionContext context, ExpressionSyntaxRewriter expressionRewriter)
+ {
+ _context = context;
+ _expressionRewriter = expressionRewriter;
+ }
+
+ ///
+ /// Attempts to convert a block statement into a single expression.
+ /// Returns null if the block contains unsupported statements.
+ ///
+ public ExpressionSyntax? TryConvertBlock(BlockSyntax block, string memberName)
+ {
+ if (block.Statements.Count == 0)
+ {
+ var diagnostic = Diagnostic.Create(
+ Diagnostics.UnsupportedStatementInBlockBody,
+ block.GetLocation(),
+ memberName,
+ "Block body must contain at least one statement"
+ );
+ _context.ReportDiagnostic(diagnostic);
+ return null;
+ }
+
+ // Try to convert the block statements into an expression
+ return TryConvertStatements(block.Statements.ToList(), memberName);
+ }
+
+ ///
+ /// Tries to convert a list of statements into a single expression. This is used for the body of the method or property.
+ ///
+ private ExpressionSyntax? TryConvertStatements(List statements, string memberName)
+ {
+ if (statements.Count == 0)
+ {
+ return null;
+ }
+
+ if (statements.Count == 1)
+ {
+ return TryConvertStatement(statements[0], memberName);
+ }
+
+ // Multiple statements - try to convert them into a chain of expressions
+ // This is done by converting local variable declarations and then the final return
+ var nonReturnStatements = statements.Take(statements.Count - 1).ToList();
+ var lastStatement = statements.Last();
+
+ // First, process any local variable declarations at the beginning
+ var localDeclStatements = new List();
+ var remainingStatements = new List();
+
+ foreach (var stmt in nonReturnStatements)
+ {
+ if (stmt is LocalDeclarationStatementSyntax localDecl)
+ {
+ localDeclStatements.Add(localDecl);
+ }
+ else
+ {
+ remainingStatements.Add(stmt);
+ }
+ }
+
+ // Process local variable declarations first
+ foreach (var localDecl in localDeclStatements)
+ {
+ if (!TryProcessLocalDeclaration(localDecl, memberName))
+ {
+ return null;
+ }
+ }
+
+ // Check if we have a pattern like multiple if statements without else followed by a final return:
+ // var x = ...; if (a) return 1; if (b) return 2; return 3;
+ // This can be converted to nested ternaries: a ? 1 : (b ? 2 : 3)
+ if (lastStatement is ReturnStatementSyntax finalReturn &&
+ remainingStatements.All(s => s is IfStatementSyntax { Else: null }))
+ {
+ // All remaining non-return statements are if statements without else
+ var ifStatements = remainingStatements.Cast().ToList();
+
+ // Start with the final return as the base expression
+ var elseBody = TryConvertReturnStatement(finalReturn, memberName);
+ if (elseBody == null)
+ {
+ return null;
+ }
+
+ // Build nested conditionals from right to left (last to first)
+ for (var i = ifStatements.Count - 1; i >= 0; i--)
+ {
+ var ifStmt = ifStatements[i];
+ var ifBody = TryConvertStatement(ifStmt.Statement, memberName);
+ if (ifBody == null)
+ {
+ return null;
+ }
+
+ // Rewrite the condition and replace any local variables
+ var condition = (ExpressionSyntax)_expressionRewriter.Visit(ifStmt.Condition);
+ condition = ReplaceLocalVariables(condition);
+
+ elseBody = SyntaxFactory.ConditionalExpression(condition, ifBody, elseBody);
+ }
+
+ return elseBody;
+ }
+
+ // If there are any remaining non-if statements, try to convert them individually
+ // This will provide better error messages for unsupported statements
+ if (remainingStatements.Count > 0)
+ {
+ // Try converting each remaining statement - this will provide specific error messages
+ foreach (var stmt in remainingStatements)
+ {
+ var converted = TryConvertStatement(stmt, memberName);
+ if (converted == null)
+ {
+ return null;
+ }
+ }
+
+ // If we got here but had non-if statements, they weren't properly handled
+ ReportUnsupportedStatement(remainingStatements[0], memberName,
+ "Only local variable declarations and if statements without else (with return) are supported before the final return statement");
+ return null;
+ }
+
+ // Convert the final statement (should be a return)
+ return TryConvertStatement(lastStatement, memberName);
+ }
+
+ ///
+ /// Processes a local variable declaration statement, rewriting the initializer and storing it in the local variables dictionary.
+ ///
+ private bool TryProcessLocalDeclaration(LocalDeclarationStatementSyntax localDecl, string memberName)
+ {
+ foreach (var variable in localDecl.Declaration.Variables)
+ {
+ if (variable.Initializer == null)
+ {
+ ReportUnsupportedStatement(localDecl, memberName, "Local variables must have an initializer");
+ return false;
+ }
+
+ var variableName = variable.Identifier.Text;
+
+ // Rewrite the initializer expression NOW while it's still in the tree
+ var rewrittenInitializer = (ExpressionSyntax)_expressionRewriter.Visit(variable.Initializer.Value);
+
+ // Also expand any previously defined local variables in this initializer
+ // This ensures transitive inlining (e.g., var a = 1; var b = a + 2; return b; -> 1 + 2)
+ rewrittenInitializer = ReplaceLocalVariables(rewrittenInitializer);
+
+ _localVariables[variableName] = rewrittenInitializer;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Tries to convert a single statement into an expression. This is used for return statements, if statements, and switch statements.
+ ///
+ private ExpressionSyntax? TryConvertStatement(StatementSyntax statement, string memberName)
+ {
+ switch (statement)
+ {
+ case ReturnStatementSyntax returnStmt:
+ return TryConvertReturnStatement(returnStmt, memberName);
+
+ case IfStatementSyntax ifStmt:
+ return TryConvertIfStatement(ifStmt, memberName);
+
+ case SwitchStatementSyntax switchStmt:
+ return TryConvertSwitchStatement(switchStmt, memberName);
+
+ case BlockSyntax blockStmt:
+ // Prevent locals declared in nested blocks from leaking into outer scopes
+ var nestedLocal = blockStmt.DescendantNodes()
+ .OfType()
+ .FirstOrDefault();
+
+ if (nestedLocal is not null)
+ {
+ ReportUnsupportedStatement(nestedLocal, memberName, "Local declarations in nested blocks are not supported");
+ return null;
+ }
+
+ return TryConvertStatements(blockStmt.Statements.ToList(), memberName);
+
+ case ExpressionStatementSyntax exprStmt:
+ // Expression statements may contain side effects - analyze them
+ return AnalyzeExpressionStatement(exprStmt, memberName);
+
+ case LocalDeclarationStatementSyntax:
+ // Local declarations should be handled before the return statement
+ ReportUnsupportedStatement(statement, memberName, "Local declarations must appear before the return statement");
+ return null;
+
+ default:
+ ReportUnsupportedStatement(statement, memberName, $"Statement type '{statement.GetType().Name}' is not supported");
+ return null;
+ }
+ }
+
+ ///
+ /// Converts a return statement to its expression, after rewriting it and replacing any local variable references.
+ ///
+ private ExpressionSyntax? TryConvertReturnStatement(ReturnStatementSyntax returnStmt, string memberName)
+ {
+ if (returnStmt.Expression == null)
+ {
+ ReportUnsupportedStatement(returnStmt, memberName, "Return statement must have an expression");
+ return null;
+ }
+
+ // First rewrite the return expression
+ var expression = (ExpressionSyntax)_expressionRewriter.Visit(returnStmt.Expression);
+
+ // Then replace any local variable references with their already-rewritten initializers
+ expression = ReplaceLocalVariables(expression);
+
+ return expression;
+ }
+
+ ///
+ /// Converts an if statement (with optional else) to a conditional expression.
+ ///
+ private ConditionalExpressionSyntax? TryConvertIfStatement(IfStatementSyntax ifStmt, string memberName)
+ {
+ // Convert if-else to conditional (ternary) expression
+ // First, rewrite the condition using the expression rewriter
+ var condition = (ExpressionSyntax)_expressionRewriter.Visit(ifStmt.Condition);
+
+ // Then replace any local variable references with their already-rewritten initializers
+ condition = ReplaceLocalVariables(condition);
+
+ var whenTrue = TryConvertStatement(ifStmt.Statement, memberName);
+ if (whenTrue == null)
+ {
+ return null;
+ }
+
+ ExpressionSyntax? whenFalse;
+ if (ifStmt.Else != null)
+ {
+ whenFalse = TryConvertStatement(ifStmt.Else.Statement, memberName);
+ if (whenFalse == null)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ // If there's no else clause, use a default literal
+ // This will be inferred to the correct type by the compiler
+ whenFalse = SyntaxFactory.LiteralExpression(
+ SyntaxKind.DefaultLiteralExpression,
+ SyntaxFactory.Token(SyntaxKind.DefaultKeyword)
+ );
+ }
+
+ // Create a conditional expression with the rewritten nodes
+ return SyntaxFactory.ConditionalExpression(
+ condition,
+ whenTrue,
+ whenFalse
+ );
+ }
+
+ ///
+ /// Converts a switch statement to nested conditional expressions.
+ ///
+ private ExpressionSyntax? TryConvertSwitchStatement(SwitchStatementSyntax switchStmt, string memberName)
+ {
+ // Convert switch statement to nested conditional expressions
+ // Process sections in reverse order to build from the default case up
+
+ var switchExpression =
+ (ExpressionSyntax)_expressionRewriter.Visit(switchStmt.Expression);
+ // Replace any local variable references in the switch expression
+ switchExpression = ReplaceLocalVariables(switchExpression);
+
+ ExpressionSyntax? currentExpression;
+
+ // Find default case first
+ SwitchSectionSyntax? defaultSection = null;
+ var nonDefaultSections = new List();
+
+ foreach (var section in switchStmt.Sections)
+ {
+ var hasDefault = section.Labels.Any(label => label is DefaultSwitchLabelSyntax);
+ if (hasDefault)
+ {
+ defaultSection = section;
+ }
+ else
+ {
+ nonDefaultSections.Add(section);
+ }
+ }
+
+ // Start with default case or null
+ if (defaultSection != null)
+ {
+ currentExpression = ConvertSwitchSection(defaultSection, memberName);
+ if (currentExpression == null)
+ {
+ return null;
+ }
+ }
+ else
+ {
+ // No default case - use default literal
+ currentExpression = SyntaxFactory.LiteralExpression(
+ SyntaxKind.DefaultLiteralExpression,
+ SyntaxFactory.Token(SyntaxKind.DefaultKeyword)
+ );
+ }
+
+ // Process non-default sections in reverse order
+ for (var i = nonDefaultSections.Count - 1; i >= 0; i--)
+ {
+ var section = nonDefaultSections[i];
+ var sectionExpression = ConvertSwitchSection(section, memberName);
+ if (sectionExpression == null)
+ {
+ return null;
+ }
+
+ // Build condition for all labels in this section (OR'd together)
+ ExpressionSyntax? condition = null;
+ foreach (var label in section.Labels)
+ {
+ if (label is CaseSwitchLabelSyntax caseLabel)
+ {
+ // Rewrite and replace locals in case label value
+ var caseLabelValue = (ExpressionSyntax)_expressionRewriter.Visit(caseLabel.Value);
+ caseLabelValue = ReplaceLocalVariables(caseLabelValue);
+
+ var labelCondition = SyntaxFactory.BinaryExpression(
+ SyntaxKind.EqualsExpression,
+ switchExpression,
+ caseLabelValue
+ );
+
+ condition = condition == null
+ ? labelCondition
+ : SyntaxFactory.BinaryExpression(
+ SyntaxKind.LogicalOrExpression,
+ condition,
+ labelCondition
+ );
+ }
+ else if (label is not DefaultSwitchLabelSyntax)
+ {
+ // Unsupported label type (e.g., pattern-based switch in older syntax)
+ ReportUnsupportedStatement(switchStmt, memberName,
+ $"Switch label type '{label.GetType().Name}' is not supported. Use case labels or switch expressions instead.");
+ return null;
+ }
+ }
+
+ if (condition != null)
+ {
+ currentExpression = SyntaxFactory.ConditionalExpression(
+ condition,
+ sectionExpression,
+ currentExpression
+ );
+ }
+ }
+
+ return currentExpression;
+ }
+
+ ///
+ /// Converts a switch section to an expression. This assumes the section has already been validated to only contain supported statements.
+ ///
+ private ExpressionSyntax? ConvertSwitchSection(SwitchSectionSyntax section, string memberName)
+ {
+ // Convert the statements in the switch section
+ var statements = section.Statements.ToList();
+
+ // Remove trailing break statements as they're not needed in expressions
+ if (statements.Count > 0 && statements.Last() is BreakStatementSyntax)
+ {
+ statements = statements.Take(statements.Count - 1).ToList();
+ }
+
+ if (statements.Count > 0)
+ {
+ return TryConvertStatements(statements, memberName);
+ }
+
+ // Empty section - report diagnostic
+ var firstLabel = section.Labels.FirstOrDefault();
+ var location = firstLabel?.GetLocation() ?? section.GetLocation();
+
+ var diagnostic = Diagnostic.Create(
+ Diagnostics.UnsupportedStatementInBlockBody,
+ location,
+ memberName,
+ "Switch section must have at least one statement"
+ );
+ _context.ReportDiagnostic(diagnostic);
+ return null;
+
+ }
+
+ ///
+ /// Replaces references to local variables in the given expression with their initializer expressions.
+ ///
+ private ExpressionSyntax ReplaceLocalVariables(ExpressionSyntax expression)
+ {
+ // Use a rewriter to replace local variable references with their initializer expressions
+ var rewriter = new LocalVariableReplacer(_localVariables);
+ return (ExpressionSyntax)rewriter.Visit(expression);
+ }
+
+ ///
+ /// Analyzes an expression statement for side effects. If it has side effects, reports a diagnostic and returns null.
+ ///
+ private ExpressionSyntax? AnalyzeExpressionStatement(ExpressionStatementSyntax exprStmt, string memberName)
+ {
+ var expression = exprStmt.Expression;
+
+ // Check for specific side effects that are always errors
+ if (HasSideEffects(expression, out var errorMessage))
+ {
+ ReportSideEffect(expression, errorMessage);
+ return null;
+ }
+
+ // Check for potentially impure method calls
+ if (expression is InvocationExpressionSyntax invocation)
+ {
+ if (!IsProjectableMethodCall(invocation, out var warningMessage))
+ {
+ ReportPotentialSideEffect(invocation, warningMessage);
+ return null;
+ }
+ }
+
+ // Expression statements without side effects are still not supported in the current design
+ ReportUnsupportedStatement(exprStmt, memberName,
+ "Expression statements are not supported in projectable methods. Consider removing this statement or converting it to a return statement.");
+ return null;
+ }
+
+ ///
+ /// Checks if an expression has side effects.
+ ///
+ private bool HasSideEffects(ExpressionSyntax expression, out string errorMessage)
+ {
+ return expression switch
+ {
+ AssignmentExpressionSyntax assignment => (errorMessage = GetAssignmentErrorMessage(assignment)) != null,
+
+ PostfixUnaryExpressionSyntax postfix when
+ postfix.IsKind(SyntaxKind.PostIncrementExpression) ||
+ postfix.IsKind(SyntaxKind.PostDecrementExpression)
+ => (errorMessage = $"Increment/decrement operator '{postfix.OperatorToken.Text}' has side effects and cannot be used in projectable methods") != null,
+
+ PrefixUnaryExpressionSyntax prefix when
+ prefix.IsKind(SyntaxKind.PreIncrementExpression) ||
+ prefix.IsKind(SyntaxKind.PreDecrementExpression)
+ => (errorMessage = $"Increment/decrement operator '{prefix.OperatorToken.Text}' has side effects and cannot be used in projectable methods") != null,
+
+ _ => (errorMessage = string.Empty) == null
+ };
+ }
+
+ ///
+ /// Checks if a method invocation is to a projectable method.
+ ///
+ private bool IsProjectableMethodCall(InvocationExpressionSyntax invocation, out string warningMessage)
+ {
+ var symbolInfo = _expressionRewriter.GetSemanticModel().GetSymbolInfo(invocation);
+ if (symbolInfo.Symbol is IMethodSymbol methodSymbol)
+ {
+ var hasProjectableAttr = methodSymbol.GetAttributes()
+ .Any(attr => attr.AttributeClass?.Name == "ProjectableAttribute");
+
+ if (!hasProjectableAttr)
+ {
+ warningMessage = $"Method call '{methodSymbol.Name}' may have side effects. Only calls to methods marked with [Projectable] are guaranteed to be safe in projectable methods";
+ return false;
+ }
+ }
+
+ warningMessage = string.Empty;
+ return true;
+ }
+
+ ///
+ /// Generates an error message for an assignment expression, indicating that it has side effects and cannot be used in projectable methods.
+ ///
+ private string GetAssignmentErrorMessage(AssignmentExpressionSyntax assignment)
+ {
+ var operatorText = assignment.OperatorToken.Text;
+
+ if (assignment.IsKind(SyntaxKind.SimpleAssignmentExpression))
+ {
+ if (assignment.Left is MemberAccessExpressionSyntax memberAccess)
+ {
+ return $"Property assignment '{memberAccess.Name}' has side effects and cannot be used in projectable methods";
+ }
+ return "Assignment operation has side effects and cannot be used in projectable methods";
+ }
+
+ // Compound assignment like +=, -=, etc.
+ return $"Compound assignment operator '{operatorText}' has side effects and cannot be used in projectable methods";
+ }
+
+ private void ReportSideEffect(SyntaxNode node, string message)
+ {
+ var diagnostic = Diagnostic.Create(
+ Diagnostics.SideEffectInBlockBody,
+ node.GetLocation(),
+ message
+ );
+ _context.ReportDiagnostic(diagnostic);
+ }
+
+ private void ReportPotentialSideEffect(SyntaxNode node, string message)
+ {
+ var diagnostic = Diagnostic.Create(
+ Diagnostics.PotentialSideEffectInBlockBody,
+ node.GetLocation(),
+ message
+ );
+ _context.ReportDiagnostic(diagnostic);
+ }
+
+ private void ReportUnsupportedStatement(StatementSyntax statement, string memberName, string reason)
+ {
+ var diagnostic = Diagnostic.Create(
+ Diagnostics.UnsupportedStatementInBlockBody,
+ statement.GetLocation(),
+ memberName,
+ reason
+ );
+ _context.ReportDiagnostic(diagnostic);
+ }
+
+ private class LocalVariableReplacer : CSharpSyntaxRewriter
+ {
+ private readonly Dictionary _localVariables;
+
+ public LocalVariableReplacer(Dictionary localVariables)
+ {
+ _localVariables = localVariables;
+ }
+
+ public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
+ {
+ var identifier = node.Identifier.Text;
+ if (_localVariables.TryGetValue(identifier, out var replacement))
+ {
+ // Replace the identifier with the expression it was initialized with,
+ // wrapping in parentheses to preserve operator precedence.
+ var parenthesized = SyntaxFactory.ParenthesizedExpression(replacement.WithoutTrivia());
+ return parenthesized.WithTriviaFrom(node);
+ }
+
+ return base.VisitIdentifierName(node);
+ }
+ }
+ }
+}
diff --git a/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs b/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs
index 18f87c1..70e2964 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs
+++ b/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs
@@ -1,20 +1,15 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
namespace EntityFrameworkCore.Projectables.Generator
{
public static class Diagnostics
{
- public static readonly DiagnosticDescriptor RequiresExpressionBodyDefinition = new DiagnosticDescriptor(
+ public static readonly DiagnosticDescriptor BlockBodyExperimental = new DiagnosticDescriptor(
id: "EFP0001",
- title: "Method or property should expose an expression body definition",
- messageFormat: "Method or property '{0}' should expose an expression body definition",
+ title: "Block-bodied member support is experimental",
+ messageFormat: "Block-bodied member '{0}' is using an experimental feature. Set AllowBlockBody = true on the Projectable attribute to suppress this warning.",
category: "Design",
- DiagnosticSeverity.Error,
+ DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public static readonly DiagnosticDescriptor NullConditionalRewriteUnsupported = new DiagnosticDescriptor(
@@ -25,5 +20,37 @@ public static class Diagnostics
DiagnosticSeverity.Error,
isEnabledByDefault: true);
+ public static readonly DiagnosticDescriptor UnsupportedStatementInBlockBody = new DiagnosticDescriptor(
+ id: "EFP0003",
+ title: "Unsupported statement in block-bodied method",
+ messageFormat: "Method '{0}' contains an unsupported statement: {1}",
+ category: "Design",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor SideEffectInBlockBody = new DiagnosticDescriptor(
+ id: "EFP0004",
+ title: "Statement with side effects in block-bodied method",
+ messageFormat: "{0}",
+ category: "Design",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor PotentialSideEffectInBlockBody = new DiagnosticDescriptor(
+ id: "EFP0005",
+ title: "Potential side effect in block-bodied method",
+ messageFormat: "{0}",
+ category: "Design",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true);
+
+ public static readonly DiagnosticDescriptor RequiresBodyDefinition = new DiagnosticDescriptor(
+ id: "EFP0006",
+ title: "Method or property should expose a body definition",
+ messageFormat: "Method or property '{0}' should expose a body definition (e.g. an expression-bodied member or a block-bodied method) to be used as the source for the generated expression tree.",
+ category: "Design",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true);
+
}
}
diff --git a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs
index f51c8f6..f953701 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs
+++ b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs
@@ -1,4 +1,4 @@
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;
@@ -26,6 +26,8 @@ public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, NullCondition
_extensionParameterName = extensionParameterName;
}
+ public SemanticModel GetSemanticModel() => _semanticModel;
+
private SyntaxNode? VisitThisBaseExpression(CSharpSyntaxNode node)
{
// Swap out the use of this and base to @this and keep leading and trailing trivias
@@ -386,8 +388,47 @@ private ExpressionSyntax CreateMethodCallOnEnumValue(IMethodSymbol methodSymbol,
continue;
}
+ // Handle relational patterns (<=, <, >=, >)
+ if (arm.Pattern is RelationalPatternSyntax relational)
+ {
+ // Map the pattern operator token to a binary expression kind
+ var binaryKind = relational.OperatorToken.Kind() switch
+ {
+ SyntaxKind.LessThanToken => SyntaxKind.LessThanExpression,
+ SyntaxKind.LessThanEqualsToken => SyntaxKind.LessThanOrEqualExpression,
+ SyntaxKind.GreaterThanToken => SyntaxKind.GreaterThanExpression,
+ SyntaxKind.GreaterThanEqualsToken => SyntaxKind.GreaterThanOrEqualExpression,
+ _ => throw new InvalidOperationException(
+ $"Unsupported relational operator in switch expression: {relational.OperatorToken.Kind()}")
+ };
+
+ var condition = SyntaxFactory.BinaryExpression(
+ binaryKind,
+ (ExpressionSyntax)Visit(node.GoverningExpression),
+ (ExpressionSyntax)Visit(relational.Expression)
+ );
+
+ // Add the when clause as a AND expression
+ if (arm.WhenClause != null)
+ {
+ condition = SyntaxFactory.BinaryExpression(
+ SyntaxKind.LogicalAndExpression,
+ condition,
+ (ExpressionSyntax)Visit(arm.WhenClause.Condition)
+ );
+ }
+
+ currentExpression = SyntaxFactory.ConditionalExpression(
+ condition,
+ armExpression,
+ currentExpression
+ );
+
+ continue;
+ }
+
throw new InvalidOperationException(
- $"Switch expressions rewriting supports only constant values and declaration patterns (Type var). " +
+ $"Switch expressions rewriting supports constant values, relational patterns (<=, <, >=, >), and declaration patterns (Type var). " +
$"Unsupported pattern: {arm.Pattern.GetType().Name}"
);
}
diff --git a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs
index 8a84310..3037fb1 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs
+++ b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs
@@ -80,6 +80,11 @@ static IEnumerable GetNestedInClassPathForExtensionMember(ITypeSymbol ex
.Select(x => x.Value.Value is bool b && b)
.FirstOrDefault();
+ var allowBlockBody = projectableAttributeClass.NamedArguments
+ .Where(x => x.Key == "AllowBlockBody")
+ .Select(x => x.Value.Value is bool b && b)
+ .FirstOrDefault();
+
var memberBody = member;
if (useMemberBody is not null)
@@ -120,22 +125,37 @@ x is IPropertySymbol xProperty &&
return false;
}
else if (x is MethodDeclarationSyntax xMethod &&
- xMethod.ExpressionBody is not null)
+ (xMethod.ExpressionBody is not null || xMethod.Body is not null))
{
return true;
}
- else if (x is PropertyDeclarationSyntax xProperty &&
- xProperty.ExpressionBody is not null)
+ else if (x is PropertyDeclarationSyntax xProperty)
{
- return true;
- }
- else
- {
- return false;
+ // Support expression-bodied properties: int Prop => value;
+ if (xProperty.ExpressionBody is not null)
+ {
+ return true;
+ }
+
+ // Support properties with explicit getters: int Prop { get => value; } or { get { return value; } }
+ if (xProperty.AccessorList is not null)
+ {
+ var getter = xProperty.AccessorList.Accessors
+ .FirstOrDefault(a => a.IsKind(SyntaxKind.GetAccessorDeclaration));
+ if (getter?.ExpressionBody is not null || getter?.Body is not null)
+ {
+ return true;
+ }
+ }
}
+
+ return false;
});
- if (memberBody is null) return null;
+ if (memberBody is null)
+ {
+ return null;
+ }
}
// Check if this member is inside a C# 14 extension block
@@ -300,11 +320,41 @@ x is IPropertySymbol xProperty &&
descriptor.TargetNestedInClassNames = descriptor.NestedInClassNames;
}
+ // Projectable methods
if (memberBody is MethodDeclarationSyntax methodDeclarationSyntax)
{
- if (methodDeclarationSyntax.ExpressionBody is null)
+ ExpressionSyntax? bodyExpression = null;
+
+ if (methodDeclarationSyntax.ExpressionBody is not null)
+ {
+ // Expression-bodied method (e.g., int Foo() => 1;)
+ bodyExpression = methodDeclarationSyntax.ExpressionBody.Expression;
+ }
+ else if (methodDeclarationSyntax.Body is not null)
{
- var diagnostic = Diagnostic.Create(Diagnostics.RequiresExpressionBodyDefinition, methodDeclarationSyntax.GetLocation(), memberSymbol.Name);
+ // Block-bodied method (e.g., int Foo() { return 1; })
+
+ // Emit warning if AllowBlockBody is not set to true
+ if (!allowBlockBody)
+ {
+ var diagnostic = Diagnostic.Create(Diagnostics.BlockBodyExperimental, methodDeclarationSyntax.GetLocation(), memberSymbol.Name);
+ context.ReportDiagnostic(diagnostic);
+ }
+
+ var blockConverter = new BlockStatementConverter(context, expressionSyntaxRewriter);
+ bodyExpression = blockConverter.TryConvertBlock(methodDeclarationSyntax.Body, memberSymbol.Name);
+
+ if (bodyExpression is null)
+ {
+ // Diagnostics already reported by BlockStatementConverter
+ return null;
+ }
+
+ // The expression has already been rewritten by BlockStatementConverter, so we don't rewrite it again
+ }
+ else
+ {
+ var diagnostic = Diagnostic.Create(Diagnostics.RequiresBodyDefinition, methodDeclarationSyntax.GetLocation(), memberSymbol.Name);
context.ReportDiagnostic(diagnostic);
return null;
}
@@ -312,7 +362,10 @@ x is IPropertySymbol xProperty &&
var returnType = declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ReturnType);
descriptor.ReturnTypeName = returnType.ToString();
- descriptor.ExpressionBody = (ExpressionSyntax)expressionSyntaxRewriter.Visit(methodDeclarationSyntax.ExpressionBody.Expression);
+ // Only rewrite expression-bodied methods, block-bodied methods are already rewritten
+ descriptor.ExpressionBody = methodDeclarationSyntax.ExpressionBody is not null
+ ? (ExpressionSyntax)expressionSyntaxRewriter.Visit(bodyExpression)
+ : bodyExpression;
foreach (var additionalParameter in ((ParameterListSyntax)declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ParameterList)).Parameters)
{
descriptor.ParametersList = descriptor.ParametersList.AddParameters(additionalParameter);
@@ -336,11 +389,56 @@ x is IPropertySymbol xProperty &&
);
}
}
+
+ // Projectable properties
else if (memberBody is PropertyDeclarationSyntax propertyDeclarationSyntax)
{
- if (propertyDeclarationSyntax.ExpressionBody is null)
+ ExpressionSyntax? bodyExpression = null;
+ var isBlockBodiedGetter = false;
+
+ // Expression-bodied property: int Prop => value;
+ if (propertyDeclarationSyntax.ExpressionBody is not null)
+ {
+ bodyExpression = propertyDeclarationSyntax.ExpressionBody.Expression;
+ }
+ else if (propertyDeclarationSyntax.AccessorList is not null)
{
- var diagnostic = Diagnostic.Create(Diagnostics.RequiresExpressionBodyDefinition, propertyDeclarationSyntax.GetLocation(), memberSymbol.Name);
+ // Property with explicit getter
+ var getter = propertyDeclarationSyntax.AccessorList.Accessors
+ .FirstOrDefault(a => a.IsKind(SyntaxKind.GetAccessorDeclaration));
+
+ if (getter?.ExpressionBody is not null)
+ {
+ // get => expression;
+ bodyExpression = getter.ExpressionBody.Expression;
+ }
+ else if (getter?.Body is not null)
+ {
+ // get { return expression; }
+ // Emit warning if AllowBlockBody is not set to true
+ if (!allowBlockBody)
+ {
+ var diagnostic = Diagnostic.Create(Diagnostics.BlockBodyExperimental, propertyDeclarationSyntax.GetLocation(), memberSymbol.Name);
+ context.ReportDiagnostic(diagnostic);
+ }
+
+ var blockConverter = new BlockStatementConverter(context, expressionSyntaxRewriter);
+ bodyExpression = blockConverter.TryConvertBlock(getter.Body, memberSymbol.Name);
+ isBlockBodiedGetter = true;
+
+ if (bodyExpression is null)
+ {
+ // Diagnostics already reported by BlockStatementConverter
+ return null;
+ }
+
+ // The expression has already been rewritten by BlockStatementConverter, so we don't rewrite it again
+ }
+ }
+
+ if (bodyExpression is null)
+ {
+ var diagnostic = Diagnostic.Create(Diagnostics.RequiresBodyDefinition, propertyDeclarationSyntax.GetLocation(), memberSymbol.Name);
context.ReportDiagnostic(diagnostic);
return null;
}
@@ -348,7 +446,11 @@ x is IPropertySymbol xProperty &&
var returnType = declarationSyntaxRewriter.Visit(propertyDeclarationSyntax.Type);
descriptor.ReturnTypeName = returnType.ToString();
- descriptor.ExpressionBody = (ExpressionSyntax)expressionSyntaxRewriter.Visit(propertyDeclarationSyntax.ExpressionBody.Expression);
+
+ // Only rewrite expression-bodied properties, block-bodied getters are already rewritten
+ descriptor.ExpressionBody = isBlockBodiedGetter
+ ? bodyExpression
+ : (ExpressionSyntax)expressionSyntaxRewriter.Visit(bodyExpression);
}
else
{
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..3eaf767
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT (CAST([e].[Value] AS float) / 100.0E0) * 50.0E0
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..3eaf767
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT (CAST([e].[Value] AS float) / 100.0E0) * 50.0E0
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..3eaf767
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.verified.txt
@@ -0,0 +1,2 @@
+SELECT (CAST([e].[Value] AS float) / 100.0E0) * 50.0E0
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..dab6bd4
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT 15
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..dab6bd4
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT 15
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..dab6bd4
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.verified.txt
@@ -0,0 +1,2 @@
+SELECT 15
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..e5b6efb
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN CAST(1 AS bit)
+ ELSE CAST(0 AS bit)
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..e5b6efb
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN CAST(1 AS bit)
+ ELSE CAST(0 AS bit)
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..e5b6efb
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN CAST(1 AS bit)
+ ELSE CAST(0 AS bit)
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..a19f725
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0 THEN [e].[Value] * 2
+ ELSE 0
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..a19f725
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0 THEN [e].[Value] * 2
+ ELSE 0
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.verified.txt
new file mode 100644
index 0000000..a19f725
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0 THEN [e].[Value] * 2
+ ELSE 0
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..ba1f2c1
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT CAST(LEN([e].[Name]) AS int)
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..ba1f2c1
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT CAST(LEN([e].[Name]) AS int)
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..ba1f2c1
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.verified.txt
@@ -0,0 +1,2 @@
+SELECT CAST(LEN([e].[Name]) AS int)
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..4d0592a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(0 AS bit) THEN N'Not Active'
+ ELSE N'Active'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..4d0592a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(0 AS bit) THEN N'Not Active'
+ ELSE N'Active'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..4d0592a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(0 AS bit) THEN N'Not Active'
+ ELSE N'Active'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet10_0.verified.txt
new file mode 100644
index 0000000..a29be77
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet10_0.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(0 AS bit) THEN 0
+ WHEN [e].[Value] < 0 THEN 0
+ ELSE [e].[Value] * 2
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet9_0.verified.txt
new file mode 100644
index 0000000..a29be77
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet9_0.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(0 AS bit) THEN 0
+ WHEN [e].[Value] < 0 THEN 0
+ ELSE [e].[Value] * 2
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.verified.txt
new file mode 100644
index 0000000..a29be77
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(0 AS bit) THEN 0
+ WHEN [e].[Value] < 0 THEN 0
+ ELSE [e].[Value] * 2
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet10_0.verified.txt
new file mode 100644
index 0000000..26ac26b
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet9_0.verified.txt
new file mode 100644
index 0000000..26ac26b
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.verified.txt
new file mode 100644
index 0000000..26ac26b
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet10_0.verified.txt
new file mode 100644
index 0000000..0c5fe1e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet10_0.verified.txt
@@ -0,0 +1,4 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN [e].[Value] * 2
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet9_0.verified.txt
new file mode 100644
index 0000000..0c5fe1e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet9_0.verified.txt
@@ -0,0 +1,4 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN [e].[Value] * 2
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.verified.txt
new file mode 100644
index 0000000..7e3c8c6
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN [e].[Value] * 2
+ ELSE NULL
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet10_0.verified.txt
new file mode 100644
index 0000000..f3f5c43
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active'
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet9_0.verified.txt
new file mode 100644
index 0000000..f3f5c43
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active'
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.verified.txt
new file mode 100644
index 0000000..f3f5c43
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active'
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet10_0.verified.txt
new file mode 100644
index 0000000..eec38d9
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + [e].[Value] * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet9_0.verified.txt
new file mode 100644
index 0000000..eec38d9
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + [e].[Value] * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.verified.txt
new file mode 100644
index 0000000..eec38d9
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + [e].[Value] * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet10_0.verified.txt
new file mode 100644
index 0000000..9689484
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + 5
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet9_0.verified.txt
new file mode 100644
index 0000000..9689484
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + 5
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.verified.txt
new file mode 100644
index 0000000..9689484
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + 5
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet10_0.verified.txt
new file mode 100644
index 0000000..257f6f0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet10_0.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] > 100 THEN N'Active High'
+ WHEN [e].[Value] > 50 THEN N'Active Medium'
+ ELSE N'Active Low'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet9_0.verified.txt
new file mode 100644
index 0000000..257f6f0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet9_0.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] > 100 THEN N'Active High'
+ WHEN [e].[Value] > 50 THEN N'Active Medium'
+ ELSE N'Active Low'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.verified.txt
new file mode 100644
index 0000000..257f6f0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] > 100 THEN N'Active High'
+ WHEN [e].[Value] > 50 THEN N'Active Medium'
+ ELSE N'Active Low'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet10_0.verified.txt
new file mode 100644
index 0000000..1ae6355
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet10_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'Very High'
+ WHEN [e].[Value] > 50 THEN N'High'
+ WHEN [e].[Value] > 10 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet9_0.verified.txt
new file mode 100644
index 0000000..1ae6355
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet9_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'Very High'
+ WHEN [e].[Value] > 50 THEN N'High'
+ WHEN [e].[Value] > 10 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.verified.txt
new file mode 100644
index 0000000..1ae6355
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'Very High'
+ WHEN [e].[Value] > 50 THEN N'High'
+ WHEN [e].[Value] > 10 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..4a903b0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + [e].[Value] * 3 + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..4a903b0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + [e].[Value] * 3 + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.verified.txt
new file mode 100644
index 0000000..4a903b0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + [e].[Value] * 3 + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet10_0.verified.txt
new file mode 100644
index 0000000..6973619
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet10_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100 THEN N'Active High'
+ WHEN [e].[IsActive] = CAST(1 AS bit) OR [e].[Value] > 50 THEN N'Active or Medium'
+ WHEN [e].[IsActive] = CAST(0 AS bit) AND [e].[Value] <= 10 THEN N'Inactive Low'
+ ELSE N'Other'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet9_0.verified.txt
new file mode 100644
index 0000000..6973619
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet9_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100 THEN N'Active High'
+ WHEN [e].[IsActive] = CAST(1 AS bit) OR [e].[Value] > 50 THEN N'Active or Medium'
+ WHEN [e].[IsActive] = CAST(0 AS bit) AND [e].[Value] <= 10 THEN N'Inactive Low'
+ ELSE N'Other'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.verified.txt
new file mode 100644
index 0000000..6973619
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100 THEN N'Active High'
+ WHEN [e].[IsActive] = CAST(1 AS bit) OR [e].[Value] > 50 THEN N'Active or Medium'
+ WHEN [e].[IsActive] = CAST(0 AS bit) AND [e].[Value] <= 10 THEN N'Inactive Low'
+ ELSE N'Other'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet10_0.verified.txt
new file mode 100644
index 0000000..9d42002
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet10_0.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ WHEN [e].[Value] > 50 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet9_0.verified.txt
new file mode 100644
index 0000000..9d42002
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet9_0.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ WHEN [e].[Value] > 50 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.verified.txt
new file mode 100644
index 0000000..9d42002
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ WHEN [e].[Value] > 50 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..5f5a209
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] = 1 THEN N'Active One'
+ WHEN [e].[Value] = 2 THEN N'Active Two'
+ ELSE N'Active Other'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..5f5a209
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] = 1 THEN N'Active One'
+ WHEN [e].[Value] = 2 THEN N'Active Two'
+ ELSE N'Active Other'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..5f5a209
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] = 1 THEN N'Active One'
+ WHEN [e].[Value] = 2 THEN N'Active Two'
+ ELSE N'Active Other'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..9d42002
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ WHEN [e].[Value] > 50 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..9d42002
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ WHEN [e].[Value] > 50 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..9d42002
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.verified.txt
@@ -0,0 +1,6 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ WHEN [e].[Value] > 50 THEN N'Medium'
+ ELSE N'Low'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..52f2a3e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT COALESCE([e].[Name], N'Unknown')
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..52f2a3e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT COALESCE([e].[Name], N'Unknown')
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..52f2a3e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.verified.txt
@@ -0,0 +1,2 @@
+SELECT COALESCE([e].[Name], N'Unknown')
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet10_0.verified.txt
new file mode 100644
index 0000000..06a56fa
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet9_0.verified.txt
new file mode 100644
index 0000000..06a56fa
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.verified.txt
new file mode 100644
index 0000000..06a56fa
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet10_0.verified.txt
new file mode 100644
index 0000000..6efc8d2
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet9_0.verified.txt
new file mode 100644
index 0000000..6efc8d2
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.verified.txt
new file mode 100644
index 0000000..6efc8d2
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.verified.txt
@@ -0,0 +1,2 @@
+SELECT 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..e6bf43e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value]
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..e6bf43e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value]
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..e6bf43e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value]
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet10_0.verified.txt
new file mode 100644
index 0000000..9ed7fa8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet10_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 THEN N'One'
+ WHEN [e].[Value] = 2 THEN N'Two'
+ WHEN [e].[Value] = 3 THEN N'Three'
+ ELSE N'Many'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet9_0.verified.txt
new file mode 100644
index 0000000..9ed7fa8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet9_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 THEN N'One'
+ WHEN [e].[Value] = 2 THEN N'Two'
+ WHEN [e].[Value] = 3 THEN N'Three'
+ ELSE N'Many'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.verified.txt
new file mode 100644
index 0000000..9ed7fa8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 THEN N'One'
+ WHEN [e].[Value] = 2 THEN N'Two'
+ WHEN [e].[Value] = 3 THEN N'Three'
+ ELSE N'Many'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet10_0.verified.txt
new file mode 100644
index 0000000..727148f
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet10_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] <= 2 THEN N'Low'
+ WHEN [e].[Value] <= 5 THEN N'Medium'
+ WHEN [e].[Value] <= 8 THEN N'High'
+ ELSE N'Critical'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet9_0.verified.txt
new file mode 100644
index 0000000..727148f
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet9_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] <= 2 THEN N'Low'
+ WHEN [e].[Value] <= 5 THEN N'Medium'
+ WHEN [e].[Value] <= 8 THEN N'High'
+ ELSE N'Critical'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.verified.txt
new file mode 100644
index 0000000..727148f
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] <= 2 THEN N'Low'
+ WHEN [e].[Value] <= 5 THEN N'Medium'
+ WHEN [e].[Value] <= 8 THEN N'High'
+ ELSE N'Critical'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet10_0.verified.txt
new file mode 100644
index 0000000..9ed7fa8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet10_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 THEN N'One'
+ WHEN [e].[Value] = 2 THEN N'Two'
+ WHEN [e].[Value] = 3 THEN N'Three'
+ ELSE N'Many'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet9_0.verified.txt
new file mode 100644
index 0000000..9ed7fa8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet9_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 THEN N'One'
+ WHEN [e].[Value] = 2 THEN N'Two'
+ WHEN [e].[Value] = 3 THEN N'Three'
+ ELSE N'Many'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.verified.txt
new file mode 100644
index 0000000..9ed7fa8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 THEN N'One'
+ WHEN [e].[Value] = 2 THEN N'Two'
+ WHEN [e].[Value] = 3 THEN N'Three'
+ ELSE N'Many'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet10_0.verified.txt
new file mode 100644
index 0000000..9c8b78e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet10_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] IN (1, 2) THEN N'Low'
+ WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium'
+ WHEN [e].[Value] IN (6, 7, 8) THEN N'High'
+ ELSE N'Critical'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet9_0.verified.txt
new file mode 100644
index 0000000..9c8b78e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet9_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] IN (1, 2) THEN N'Low'
+ WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium'
+ WHEN [e].[Value] IN (6, 7, 8) THEN N'High'
+ ELSE N'Critical'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.verified.txt
new file mode 100644
index 0000000..9c8b78e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] IN (1, 2) THEN N'Low'
+ WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium'
+ WHEN [e].[Value] IN (6, 7, 8) THEN N'High'
+ ELSE N'Critical'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..f2343d3
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active One'
+ WHEN [e].[Value] = 1 THEN N'Inactive One'
+ WHEN [e].[Value] > 10 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active High'
+ ELSE N'Other'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..f2343d3
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active One'
+ WHEN [e].[Value] = 1 THEN N'Inactive One'
+ WHEN [e].[Value] > 10 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active High'
+ ELSE N'Other'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..f2343d3
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.verified.txt
@@ -0,0 +1,7 @@
+SELECT CASE
+ WHEN [e].[Value] = 1 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active One'
+ WHEN [e].[Value] = 1 THEN N'Inactive One'
+ WHEN [e].[Value] > 10 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active High'
+ ELSE N'Other'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet10_0.verified.txt
new file mode 100644
index 0000000..f3f5c43
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active'
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet9_0.verified.txt
new file mode 100644
index 0000000..f3f5c43
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active'
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.verified.txt
new file mode 100644
index 0000000..f3f5c43
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active'
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.cs
new file mode 100644
index 0000000..98320b3
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.cs
@@ -0,0 +1,672 @@
+using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
+using Microsoft.EntityFrameworkCore;
+using System.Linq;
+using System.Threading.Tasks;
+using VerifyXunit;
+using Xunit;
+
+namespace EntityFrameworkCore.Projectables.FunctionalTests.BlockBodiedMethods
+{
+ [UsesVerify]
+ public class BlockBodiedMethodTests
+ {
+ public record Entity
+ {
+ public int Id { get; set; }
+ public int Value { get; set; }
+ public bool IsActive { get; set; }
+ public string? Name { get; set; }
+ }
+
+ [Fact]
+ public Task SimpleReturn_IsTranslatedToSql()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetConstant());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task ReturnWithPropertyAccess_IsTranslatedToSql()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetValuePlusTen());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task IfElseStatement_IsTranslatedToTernary()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetCategory());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task NestedIfElse_IsTranslatedToNestedTernary()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetLevel());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task LocalVariable_IsInlined()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.CalculateDouble());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task ComplexConditional_IsTranslatedCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetAdjustedValue());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockMethodWithParameters_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.Add(5, 10));
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task IfWithoutElse_UsesDefault()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetPremiumIfActive());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task IfWithoutElse_WithFallbackReturn()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetStatus());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task SwitchStatement_Simple()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetValueLabel());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task SwitchStatement_WithMultipleCases()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetPriority());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task MultipleEarlyReturns_ConvertedToNestedTernaries()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetValueCategory());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task NullCoalescing_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetNameOrDefault());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task ConditionalAccess_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetNameLength());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task SwitchExpression_Simple()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetValueLabelModern());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task SwitchExpression_WithDiscard()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetPriorityModern());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task MultipleLocalVariables_AreInlinedCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.CalculateComplex());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task NestedConditionals_WithLogicalOperators()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetComplexCategory());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task GuardClause_WithEarlyReturn()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetGuardedValue());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task NestedSwitchInIf_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetCombinedLogic());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task TernaryExpression_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetValueUsingTernary());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task NestedTernary_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetNestedTernary());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task MixedIfAndSwitch_WithMultiplePatterns()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetComplexMix());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task SwitchWithWhenClause_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetValueWithCondition());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task LocalVariableReuse_IsInlinedMultipleTimes()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.CalculateWithReuse());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BooleanReturn_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.IsHighValue());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task ConditionalWithNegation_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetInactiveStatus());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task StringInterpolation_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetFormattedValue());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task ArithmeticInReturn_WorksCorrectly()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.CalculatePercentage());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+ }
+
+ public static class EntityExtensions
+ {
+ [Projectable(AllowBlockBody = true)]
+ public static int GetConstant(this BlockBodiedMethodTests.Entity entity)
+ {
+ return 42;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int GetValuePlusTen(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.Value + 10;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetCategory(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.Value > 100)
+ {
+ return "High";
+ }
+ else
+ {
+ return "Low";
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetLevel(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.Value > 100)
+ {
+ return "High";
+ }
+ else if (entity.Value > 50)
+ {
+ return "Medium";
+ }
+ else
+ {
+ return "Low";
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int CalculateDouble(this BlockBodiedMethodTests.Entity entity)
+ {
+ var doubled = entity.Value * 2;
+ return doubled + 5;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int GetAdjustedValue(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.IsActive && entity.Value > 0)
+ {
+ return entity.Value * 2;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int Add(this BlockBodiedMethodTests.Entity entity, int a, int b)
+ {
+ return a + b;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int? GetPremiumIfActive(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.IsActive)
+ {
+ return entity.Value * 2;
+ }
+ return null;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetStatus(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.IsActive)
+ {
+ return "Active";
+ }
+ return "Inactive";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetValueLabel(this BlockBodiedMethodTests.Entity entity)
+ {
+ switch (entity.Value)
+ {
+ case 1:
+ return "One";
+ case 2:
+ return "Two";
+ case 3:
+ return "Three";
+ default:
+ return "Many";
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetPriority(this BlockBodiedMethodTests.Entity entity)
+ {
+ switch (entity.Value)
+ {
+ case 1:
+ case 2:
+ return "Low";
+ case 3:
+ case 4:
+ case 5:
+ return "Medium";
+ case 6:
+ case 7:
+ case 8:
+ return "High";
+ default:
+ return "Critical";
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetValueCategory(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.Value > 100)
+ {
+ return "Very High";
+ }
+
+ if (entity.Value > 50)
+ {
+ return "High";
+ }
+
+ if (entity.Value > 10)
+ {
+ return "Medium";
+ }
+
+ return "Low";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetNameOrDefault(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.Name ?? "Unknown";
+ }
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite, AllowBlockBody = true)]
+ public static int? GetNameLength(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.Name?.Length;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetValueLabelModern(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.Value switch
+ {
+ 1 => "One",
+ 2 => "Two",
+ 3 => "Three",
+ _ => "Many"
+ };
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetPriorityModern(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.Value switch
+ {
+ <= 2 => "Low",
+ <= 5 => "Medium",
+ <= 8 => "High",
+ _ => "Critical"
+ };
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int CalculateComplex(this BlockBodiedMethodTests.Entity entity)
+ {
+ var doubled = entity.Value * 2;
+ var tripled = entity.Value * 3;
+ var sum = doubled + tripled;
+ return sum + 10;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetComplexCategory(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.IsActive && entity.Value > 100)
+ {
+ return "Active High";
+ }
+
+ if (entity.IsActive || entity.Value > 50)
+ {
+ return "Active or Medium";
+ }
+
+ if (!entity.IsActive && entity.Value <= 10)
+ {
+ return "Inactive Low";
+ }
+
+ return "Other";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int GetGuardedValue(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (!entity.IsActive)
+ {
+ return 0;
+ }
+
+ if (entity.Value < 0)
+ {
+ return 0;
+ }
+
+ return entity.Value * 2;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetCombinedLogic(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.IsActive)
+ {
+ switch (entity.Value)
+ {
+ case 1:
+ return "Active One";
+ case 2:
+ return "Active Two";
+ default:
+ return "Active Other";
+ }
+ }
+
+ return "Inactive";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetValueUsingTernary(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.IsActive ? "Active" : "Inactive";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetNestedTernary(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.Value > 100 ? "High" : entity.Value > 50 ? "Medium" : "Low";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetComplexMix(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.IsActive)
+ {
+ return entity.Value switch
+ {
+ > 100 => "Active High",
+ > 50 => "Active Medium",
+ _ => "Active Low"
+ };
+ }
+
+ return "Inactive";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetValueWithCondition(this BlockBodiedMethodTests.Entity entity)
+ {
+ return entity.Value switch
+ {
+ 1 when entity.IsActive => "Active One",
+ 1 => "Inactive One",
+ > 10 when entity.IsActive => "Active High",
+ _ => "Other"
+ };
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int CalculateWithReuse(this BlockBodiedMethodTests.Entity entity)
+ {
+ var doubled = entity.Value * 2;
+ return doubled + doubled;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static bool IsHighValue(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (entity.Value > 100)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetInactiveStatus(this BlockBodiedMethodTests.Entity entity)
+ {
+ if (!entity.IsActive)
+ {
+ return "Not Active";
+ }
+ else
+ {
+ return "Active";
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetFormattedValue(this BlockBodiedMethodTests.Entity entity)
+ {
+ return $"Value: {entity.Value}";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static double CalculatePercentage(this BlockBodiedMethodTests.Entity entity)
+ {
+ return (double)entity.Value / 100.0 * 50.0;
+ }
+ }
+}
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTest.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTest.cs
new file mode 100644
index 0000000..99cbb3e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTest.cs
@@ -0,0 +1,273 @@
+using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
+using Microsoft.EntityFrameworkCore;
+using System.Linq;
+using System.Threading.Tasks;
+using VerifyXunit;
+using Xunit;
+
+namespace EntityFrameworkCore.Projectables.FunctionalTests.BlockBodiedMethods
+{
+ ///
+ /// Tests for calling projectable methods from within block-bodied methods
+ ///
+ [UsesVerify]
+ public class BlockBodyProjectableCallTests
+ {
+ public record Entity
+ {
+ public int Id { get; set; }
+ public int Value { get; set; }
+ public bool IsActive { get; set; }
+ public string? Name { get; set; }
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_Simple()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetAdjustedWithConstant());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_InReturn()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetDoubledValue());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_InCondition()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetCategoryBasedOnAdjusted());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_Multiple()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.CombineProjectableMethods());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_InSwitch()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetLabelBasedOnCategory());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_InSwitchExpression()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetDescriptionByLevel());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_WithLocalVariable()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.CalculateUsingProjectable());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_Nested()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetNestedProjectableCall());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_InEarlyReturn()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetStatusWithProjectableCheck());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_InTernary()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.GetConditionalProjectable());
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task BlockBodyCallingProjectableMethod_InLogicalExpression()
+ {
+ using var dbContext = new SampleDbContext();
+ var query = dbContext.Set()
+ .Select(x => x.IsComplexCondition());
+ return Verifier.Verify(query.ToQueryString());
+ }
+ }
+
+ public static class ProjectableCallExtensions
+ {
+ // Base projectable methods (helper methods)
+ [Projectable(AllowBlockBody = true)]
+ public static int GetConstant(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return 42;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int GetDoubled(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.Value * 2;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetCategory(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ if (entity.Value > 100)
+ return "High";
+ else
+ return "Low";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetLevel(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ if (entity.Value > 100) return "Level3";
+ if (entity.Value > 50) return "Level2";
+ return "Level1";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static bool IsHighValue(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.Value > 100;
+ }
+
+ // Block-bodied methods calling projectable methods
+
+ [Projectable(AllowBlockBody = true)]
+ public static int GetAdjustedWithConstant(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.Value + entity.GetConstant();
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int GetDoubledValue(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ var doubled = entity.GetDoubled();
+ return doubled;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetCategoryBasedOnAdjusted(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ if (entity.GetDoubled() > 200)
+ {
+ return "Very High";
+ }
+ else
+ {
+ return "Normal";
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int CombineProjectableMethods(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.GetDoubled() + entity.GetConstant();
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetLabelBasedOnCategory(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ switch (entity.GetCategory())
+ {
+ case "High":
+ return "Premium";
+ case "Low":
+ return "Standard";
+ default:
+ return "Unknown";
+ }
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetDescriptionByLevel(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.GetLevel() switch
+ {
+ "Level3" => "Expert",
+ "Level2" => "Intermediate",
+ "Level1" => "Beginner",
+ _ => "Unknown"
+ };
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int CalculateUsingProjectable(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ var doubled = entity.GetDoubled();
+ var withConstant = doubled + entity.GetConstant();
+ return withConstant * 2;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static int GetNestedProjectableCall(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.GetAdjustedWithConstant() + 10;
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetStatusWithProjectableCheck(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ if (entity.IsHighValue())
+ return "Premium";
+
+ if (entity.GetCategory() == "High")
+ return "Standard High";
+
+ return "Normal";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetConditionalProjectable(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.IsActive ? entity.GetCategory() : "Inactive";
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static string GetChainedResult(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ var doubled = entity.GetDoubled();
+
+ if (doubled > 200)
+ {
+ return entity.GetCategory() + " Priority";
+ }
+
+ return entity.GetLevel();
+ }
+
+ [Projectable(AllowBlockBody = true)]
+ public static bool IsComplexCondition(this BlockBodyProjectableCallTests.Entity entity)
+ {
+ return entity.IsActive && entity.IsHighValue() || entity.GetDoubled() > 150;
+ }
+ }
+}
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet10_0.verified.txt
new file mode 100644
index 0000000..478d0ba
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] * 2 > 200 THEN N'Very High'
+ ELSE N'Normal'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet9_0.verified.txt
new file mode 100644
index 0000000..478d0ba
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] * 2 > 200 THEN N'Very High'
+ ELSE N'Normal'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.verified.txt
new file mode 100644
index 0000000..478d0ba
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN [e].[Value] * 2 > 200 THEN N'Very High'
+ ELSE N'Normal'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet10_0.verified.txt
new file mode 100644
index 0000000..bd650a0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet10_0.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'Premium'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'High' THEN N'Standard High'
+ ELSE N'Normal'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet9_0.verified.txt
new file mode 100644
index 0000000..bd650a0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet9_0.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'Premium'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'High' THEN N'Standard High'
+ ELSE N'Normal'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.verified.txt
new file mode 100644
index 0000000..bd650a0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.verified.txt
@@ -0,0 +1,9 @@
+SELECT CASE
+ WHEN [e].[Value] > 100 THEN N'Premium'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'High' THEN N'Standard High'
+ ELSE N'Normal'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet10_0.verified.txt
new file mode 100644
index 0000000..de3373a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet10_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN ([e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100) OR [e].[Value] * 2 > 150 THEN CAST(1 AS bit)
+ ELSE CAST(0 AS bit)
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet9_0.verified.txt
new file mode 100644
index 0000000..de3373a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet9_0.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN ([e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100) OR [e].[Value] * 2 > 150 THEN CAST(1 AS bit)
+ ELSE CAST(0 AS bit)
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.verified.txt
new file mode 100644
index 0000000..de3373a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.verified.txt
@@ -0,0 +1,5 @@
+SELECT CASE
+ WHEN ([e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100) OR [e].[Value] * 2 > 150 THEN CAST(1 AS bit)
+ ELSE CAST(0 AS bit)
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet10_0.verified.txt
new file mode 100644
index 0000000..dea1914
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet9_0.verified.txt
new file mode 100644
index 0000000..dea1914
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.verified.txt
new file mode 100644
index 0000000..dea1914
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet10_0.verified.txt
new file mode 100644
index 0000000..927c6ff
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet10_0.verified.txt
@@ -0,0 +1,12 @@
+SELECT CASE
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'High' THEN N'Premium'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'Low' THEN N'Standard'
+ ELSE N'Unknown'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet9_0.verified.txt
new file mode 100644
index 0000000..927c6ff
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet9_0.verified.txt
@@ -0,0 +1,12 @@
+SELECT CASE
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'High' THEN N'Premium'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'Low' THEN N'Standard'
+ ELSE N'Unknown'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.verified.txt
new file mode 100644
index 0000000..927c6ff
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.verified.txt
@@ -0,0 +1,12 @@
+SELECT CASE
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'High' THEN N'Premium'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END = N'Low' THEN N'Standard'
+ ELSE N'Unknown'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet10_0.verified.txt
new file mode 100644
index 0000000..409a445
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet10_0.verified.txt
@@ -0,0 +1,19 @@
+SELECT CASE
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level3' THEN N'Expert'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level2' THEN N'Intermediate'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level1' THEN N'Beginner'
+ ELSE N'Unknown'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet9_0.verified.txt
new file mode 100644
index 0000000..409a445
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet9_0.verified.txt
@@ -0,0 +1,19 @@
+SELECT CASE
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level3' THEN N'Expert'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level2' THEN N'Intermediate'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level1' THEN N'Beginner'
+ ELSE N'Unknown'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.verified.txt
new file mode 100644
index 0000000..409a445
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.verified.txt
@@ -0,0 +1,19 @@
+SELECT CASE
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level3' THEN N'Expert'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level2' THEN N'Intermediate'
+ WHEN CASE
+ WHEN [e].[Value] > 100 THEN N'Level3'
+ WHEN [e].[Value] > 50 THEN N'Level2'
+ ELSE N'Level1'
+ END = N'Level1' THEN N'Beginner'
+ ELSE N'Unknown'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet10_0.verified.txt
new file mode 100644
index 0000000..ad971d0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet10_0.verified.txt
@@ -0,0 +1,8 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet9_0.verified.txt
new file mode 100644
index 0000000..ad971d0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet9_0.verified.txt
@@ -0,0 +1,8 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.verified.txt
new file mode 100644
index 0000000..ad971d0
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.verified.txt
@@ -0,0 +1,8 @@
+SELECT CASE
+ WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE
+ WHEN [e].[Value] > 100 THEN N'High'
+ ELSE N'Low'
+ END
+ ELSE N'Inactive'
+END
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet10_0.verified.txt
new file mode 100644
index 0000000..69eb4b8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet9_0.verified.txt
new file mode 100644
index 0000000..69eb4b8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.verified.txt
new file mode 100644
index 0000000..69eb4b8
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] * 2 + 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet10_0.verified.txt
new file mode 100644
index 0000000..72fc7ea
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 42 + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet9_0.verified.txt
new file mode 100644
index 0000000..72fc7ea
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 42 + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.verified.txt
new file mode 100644
index 0000000..72fc7ea
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 42 + 10
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet10_0.verified.txt
new file mode 100644
index 0000000..0bb6121
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet9_0.verified.txt
new file mode 100644
index 0000000..0bb6121
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.verified.txt
new file mode 100644
index 0000000..0bb6121
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Value] + 42
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet10_0.verified.txt
new file mode 100644
index 0000000..ae5ad93
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet10_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT ([e].[Value] * 2 + 42) * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet9_0.verified.txt
new file mode 100644
index 0000000..ae5ad93
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet9_0.verified.txt
@@ -0,0 +1,2 @@
+SELECT ([e].[Value] * 2 + 42) * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.verified.txt
new file mode 100644
index 0000000..ae5ad93
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.verified.txt
@@ -0,0 +1,2 @@
+SELECT ([e].[Value] * 2 + 42) * 2
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_CompoundAssignment_ReportsError.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_CompoundAssignment_ReportsError.verified.txt
new file mode 100644
index 0000000..a6b0b53
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_CompoundAssignment_ReportsError.verified.txt
@@ -0,0 +1,3 @@
+[
+ (11,13): error EFP0004: Compound assignment operator '+=' has side effects and cannot be used in projectable methods
+]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_ImplicitReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_ImplicitReturn.verified.txt
new file mode 100644
index 0000000..b5f9f5b
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_ImplicitReturn.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar > 10 ? 1 : default;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_UsesDefault.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_UsesDefault.verified.txt
new file mode 100644
index 0000000..c22d885
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_UsesDefault.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar > 10 ? 1 : 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IncrementOperator_ReportsError.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IncrementOperator_ReportsError.verified.txt
new file mode 100644
index 0000000..d47a3ba
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IncrementOperator_ReportsError.verified.txt
@@ -0,0 +1,3 @@
+[
+ (12,13): error EFP0004: Increment/decrement operator '++' has side effects and cannot be used in projectable methods
+]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInIfCondition.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInIfCondition.verified.txt
new file mode 100644
index 0000000..47b44c4
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInIfCondition.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => (@this.Bar * 2) > 10 ? 1 : 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInSwitchExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInSwitchExpression.verified.txt
new file mode 100644
index 0000000..ce11b5b
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInSwitchExpression.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => (@this.Bar * 2) == 2 ? "Two" : (@this.Bar * 2) == 4 ? "Four" : "Other";
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic.verified.txt
new file mode 100644
index 0000000..587f792
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic.verified.txt
@@ -0,0 +1,3 @@
+[
+ (13,17): warning EFP0003: Method 'Foo' contains an unsupported statement: Local declarations in nested blocks are not supported
+]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning.verified.txt
new file mode 100644
index 0000000..26e6a19
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning.verified.txt
@@ -0,0 +1,3 @@
+[
+ (11,13): warning EFP0005: Method call 'WriteLine' may have side effects. Only calls to methods marked with [Projectable] are guaranteed to be safe in projectable methods
+]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_PropertyAssignment_ReportsError.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_PropertyAssignment_ReportsError.verified.txt
new file mode 100644
index 0000000..e684d40
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_PropertyAssignment_ReportsError.verified.txt
@@ -0,0 +1,3 @@
+[
+ (11,13): error EFP0004: Assignment operation has side effects and cannot be used in projectable methods
+]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SimpleReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SimpleReturn.verified.txt
new file mode 100644
index 0000000..eeb0754
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SimpleReturn.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => 42;
+ }
+ }
+}
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_Simple.verified.txt
new file mode 100644
index 0000000..d1a7eb5
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_Simple.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar == 1 ? "One" : @this.Bar == 2 ? "Two" : "Other";
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithMultipleCases.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithMultipleCases.verified.txt
new file mode 100644
index 0000000..c90d6b7
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithMultipleCases.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar == 1 || @this.Bar == 2 ? "Low" : @this.Bar == 3 || @this.Bar == 4 || @this.Bar == 5 ? "Medium" : "High";
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithoutDefault.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithoutDefault.verified.txt
new file mode 100644
index 0000000..0a4d15d
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithoutDefault.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar == 1 ? "One" : @this.Bar == 2 ? "Two" : default;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElse.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElse.verified.txt
new file mode 100644
index 0000000..c22d885
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElse.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar > 10 ? 1 : 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElseAndCondition.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElseAndCondition.verified.txt
new file mode 100644
index 0000000..ef8f31a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElseAndCondition.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.IsActive && @this.Bar > 0 ? @this.Bar * 2 : 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithLocalVariable.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithLocalVariable.verified.txt
new file mode 100644
index 0000000..44c2e0f
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithLocalVariable.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => (@this.Bar * 2) + 5;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithMultipleParameters.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithMultipleParameters.verified.txt
new file mode 100644
index 0000000..7c1426a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithMultipleParameters.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Add_P0_int_P1_int
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this, int a, int b) => a + b;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithNestedIfElse.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithNestedIfElse.verified.txt
new file mode 100644
index 0000000..216b8f2
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithNestedIfElse.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar > 10 ? "High" : @this.Bar > 5 ? "Medium" : "Low";
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithPropertyAccess.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithPropertyAccess.verified.txt
new file mode 100644
index 0000000..19e29c9
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithPropertyAccess.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar + 10;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithTransitiveLocalVariables.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithTransitiveLocalVariables.verified.txt
new file mode 100644
index 0000000..3e8b98c
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithTransitiveLocalVariables.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => ((@this.Bar * 2) + 5) + 10;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableComputedPropertyWithExplicitBlockGetter.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableComputedPropertyWithExplicitBlockGetter.verified.txt
new file mode 100644
index 0000000..1614e52
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableComputedPropertyWithExplicitBlockGetter.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar + 1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableComputedPropertyWithExplicitExpressionGetter.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableComputedPropertyWithExplicitExpressionGetter.verified.txt
new file mode 100644
index 0000000..1614e52
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectableComputedPropertyWithExplicitExpressionGetter.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar + 1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetter.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetter.verified.txt
new file mode 100644
index 0000000..c9f2bbb
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetter.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => 1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetterAndMethodCall.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetterAndMethodCall.verified.txt
new file mode 100644
index 0000000..fb4be05
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetterAndMethodCall.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetterUsingThis.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetterUsingThis.verified.txt
new file mode 100644
index 0000000..3ad21d6
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitBlockGetterUsingThis.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => @this.Bar;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitExpressionGetter.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitExpressionGetter.verified.txt
new file mode 100644
index 0000000..c9f2bbb
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.ProjectablePropertyWithExplicitExpressionGetter.verified.txt
@@ -0,0 +1,17 @@
+//
+#nullable disable
+using System;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+{
+ [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
+ static class Foo_C_Foo
+ {
+ static global::System.Linq.Expressions.Expression> Expression()
+ {
+ return (global::Foo.C @this) => 1;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs
index e5b07d2..ea03112 100644
--- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs
@@ -201,6 +201,179 @@ class C {
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
+ [Fact]
+ public Task ProjectablePropertyWithExplicitExpressionGetter()
+ {
+ // Tests explicit getter with expression body: { get => expression; }
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ [Projectable]
+ public int Foo { get => 1; }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task ProjectablePropertyWithExplicitBlockGetter()
+ {
+ // Tests explicit getter with block body: { get { return expression; } }
+ // Requires AllowBlockBody = true
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ [Projectable(AllowBlockBody = true)]
+ public int Foo { get { return 1; } }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task ProjectableComputedPropertyWithExplicitExpressionGetter()
+ {
+ // Tests explicit getter with expression body accessing other properties
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable]
+ public int Foo { get => Bar + 1; }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task ProjectableComputedPropertyWithExplicitBlockGetter()
+ {
+ // Tests explicit getter with block body accessing other properties
+ // Requires AllowBlockBody = true
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo { get { return Bar + 1; } }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task ProjectablePropertyWithExplicitBlockGetterUsingThis()
+ {
+ // Tests explicit getter with block body using 'this' qualifier
+ // Requires AllowBlockBody = true
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo { get { return this.Bar; } }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task ProjectablePropertyWithExplicitBlockGetterAndMethodCall()
+ {
+ // Tests explicit getter with block body calling other methods
+ // Requires AllowBlockBody = true
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar() => 1;
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo { get { return Bar(); } }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public void ProjectablePropertyWithExplicitBlockGetter_WithoutAllowBlockBody_EmitsWarning()
+ {
+ // Tests that block-bodied property getter without AllowBlockBody = true emits a warning
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ [Projectable]
+ public int Foo { get { return 1; } }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ // Should have a warning about experimental feature
+ var diagnostic = Assert.Single(result.Diagnostics);
+ Assert.Equal("EFP0001", diagnostic.Id);
+ Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity);
+ }
+
[Fact]
public Task MoreComplexProjectableComputedProperty()
@@ -471,36 +644,14 @@ static class C {
}
[Fact]
- public void BlockBodiedMember_RaisesDiagnostics()
- {
- var compilation = CreateCompilation(@"
-using System;
-using EntityFrameworkCore.Projectables;
-namespace Foo {
- class C {
- [Projectable]
- public int Foo
- {
- get => 1;
- }
- }
-}
-");
-
- var result = RunGenerator(compilation);
-
- Assert.Single(result.Diagnostics);
- }
-
- [Fact]
- public void BlockBodiedMethod_RaisesDiagnostics()
+ public void BlockBodiedMethod_NoLongerRaisesDiagnostics()
{
var compilation = CreateCompilation(@"
using System;
using EntityFrameworkCore.Projectables;
namespace Foo {
class C {
- [Projectable]
+ [Projectable(AllowBlockBody = true)]
public int Foo()
{
return 1;
@@ -511,7 +662,9 @@ public int Foo()
var result = RunGenerator(compilation);
- Assert.Single(result.Diagnostics);
+ // Block-bodied methods are now supported, so no diagnostics should be raised
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
}
[Fact]
@@ -1978,19 +2131,18 @@ public static Dictionary ToDictionary(this Entity entity)
}
[Fact]
- public Task MethodOverloads_WithDifferentParameterTypes()
+ public Task BlockBodiedMethod_SimpleReturn()
{
- // lang=csharp
var compilation = CreateCompilation(@"
using System;
using EntityFrameworkCore.Projectables;
namespace Foo {
class C {
- [Projectable]
- public int Method(int x) => x;
-
- [Projectable]
- public int Method(string s) => s.Length;
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ return 42;
+ }
}
}
");
@@ -1998,30 +2150,26 @@ class C {
var result = RunGenerator(compilation);
Assert.Empty(result.Diagnostics);
- Assert.Equal(2, result.GeneratedTrees.Length);
-
- // Verify both overloads are generated with distinct names
- var generatedFiles = result.GeneratedTrees.Select(t => t.FilePath).ToList();
- Assert.Contains(generatedFiles, f => f.Contains("Method_P0_int.g.cs"));
- Assert.Contains(generatedFiles, f => f.Contains("Method_P0_string.g.cs"));
+ Assert.Single(result.GeneratedTrees);
- return Verifier.Verify(result.GeneratedTrees.Select(t => t.ToString()));
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
-
+
[Fact]
- public Task MethodOverloads_WithDifferentParameterCounts()
+ public Task BlockBodiedMethod_WithPropertyAccess()
{
- // lang=csharp
var compilation = CreateCompilation(@"
using System;
using EntityFrameworkCore.Projectables;
namespace Foo {
class C {
- [Projectable]
- public int Method(int x) => x;
-
- [Projectable]
- public int Method(int x, int y) => x + y;
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ return Bar + 10;
+ }
}
}
");
@@ -2029,33 +2177,32 @@ class C {
var result = RunGenerator(compilation);
Assert.Empty(result.Diagnostics);
- Assert.Equal(2, result.GeneratedTrees.Length);
-
- // Verify both overloads are generated with distinct names
- var generatedFiles = result.GeneratedTrees.Select(t => t.FilePath).ToList();
- Assert.Contains(generatedFiles, f => f.Contains("Method_P0_int.g.cs"));
- Assert.Contains(generatedFiles, f => f.Contains("Method_P0_int_P1_int.g.cs"));
+ Assert.Single(result.GeneratedTrees);
- return Verifier.Verify(result.GeneratedTrees.Select(t => t.ToString()));
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
-#if NET10_0_OR_GREATER
[Fact]
- public Task ExtensionMemberProperty()
+ public Task BlockBodiedMethod_WithIfElse()
{
var compilation = CreateCompilation(@"
using System;
using EntityFrameworkCore.Projectables;
-
namespace Foo {
- class Entity {
- public int Id { get; set; }
- }
-
- static class EntityExtensions {
- extension(Entity e) {
- [Projectable]
- public int DoubleId => e.Id * 2;
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ if (Bar > 10)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
}
}
}
@@ -2070,21 +2217,30 @@ static class EntityExtensions {
}
[Fact]
- public Task ExtensionMemberMethod()
+ public Task BlockBodiedMethod_WithNestedIfElse()
{
var compilation = CreateCompilation(@"
using System;
using EntityFrameworkCore.Projectables;
-
namespace Foo {
- class Entity {
- public int Id { get; set; }
- }
-
- static class EntityExtensions {
- extension(Entity e) {
- [Projectable]
- public int TripleId() => e.Id * 3;
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public string Foo()
+ {
+ if (Bar > 10)
+ {
+ return ""High"";
+ }
+ else if (Bar > 5)
+ {
+ return ""Medium"";
+ }
+ else
+ {
+ return ""Low"";
+ }
}
}
}
@@ -2099,20 +2255,647 @@ static class EntityExtensions {
}
[Fact]
- public Task ExtensionMemberMethodWithParameters()
+ public Task BlockBodiedMethod_WithLocalVariable()
{
var compilation = CreateCompilation(@"
using System;
using EntityFrameworkCore.Projectables;
-
namespace Foo {
- class Entity {
- public int Id { get; set; }
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ var temp = Bar * 2;
+ return temp + 5;
+ }
}
-
- static class EntityExtensions {
- extension(Entity e) {
- [Projectable]
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_WithTransitiveLocalVariables()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ var a = Bar * 2;
+ var b = a + 5;
+ return b + 10;
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_LocalInIfCondition()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ var threshold = Bar * 2;
+ if (threshold > 10)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_LocalInSwitchExpression()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public string Foo()
+ {
+ var value = Bar * 2;
+ switch (value)
+ {
+ case 2:
+ return ""Two"";
+ case 4:
+ return ""Four"";
+ default:
+ return ""Other"";
+ }
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ if (Bar > 10)
+ {
+ var temp = Bar * 2;
+ return temp;
+ }
+ return 0;
+ }
+ }
+}
+", expectedToCompile: true);
+
+ var result = RunGenerator(compilation);
+
+ // Should have a diagnostic about locals in nested blocks
+ Assert.NotEmpty(result.Diagnostics);
+ Assert.Contains(result.Diagnostics, d => d.Id == "EFP0003");
+
+ return Verifier.Verify(result.Diagnostics.Select(d => d.ToString()));
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_WithMultipleParameters()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ [Projectable(AllowBlockBody = true)]
+ public int Add(int a, int b)
+ {
+ return a + b;
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_WithIfElseAndCondition()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+ public bool IsActive { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ if (IsActive && Bar > 0)
+ {
+ return Bar * 2;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+
+ [Fact]
+ public Task BlockBodiedMethod_IfWithoutElse_UsesDefault()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ if (Bar > 10)
+ {
+ return 1;
+ }
+ return 0;
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_IfWithoutElse_ImplicitReturn()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int? Foo()
+ {
+ if (Bar > 10)
+ {
+ return 1;
+ }
+ }
+ }
+}
+", expectedToCompile: false);
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_SwitchStatement_Simple()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public string Foo()
+ {
+ switch (Bar)
+ {
+ case 1:
+ return ""One"";
+ case 2:
+ return ""Two"";
+ default:
+ return ""Other"";
+ }
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_SwitchStatement_WithMultipleCases()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public string Foo()
+ {
+ switch (Bar)
+ {
+ case 1:
+ case 2:
+ return ""Low"";
+ case 3:
+ case 4:
+ case 5:
+ return ""Medium"";
+ default:
+ return ""High"";
+ }
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_SwitchStatement_WithoutDefault()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public string? Foo()
+ {
+ switch (Bar)
+ {
+ case 1:
+ return ""One"";
+ case 2:
+ return ""Two"";
+ }
+ }
+ }
+}
+", expectedToCompile: false);
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_PropertyAssignment_ReportsError()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ Bar = 10;
+ return Bar;
+ }
+ }
+}
+", expectedToCompile: true);
+
+ var result = RunGenerator(compilation);
+
+ // Should have a diagnostic about side effects
+ Assert.NotEmpty(result.Diagnostics);
+ Assert.Contains(result.Diagnostics, d => d.Id == "EFP0004");
+
+ return Verifier.Verify(result.Diagnostics.Select(d => d.ToString()));
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_CompoundAssignment_ReportsError()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ Bar += 10;
+ return Bar;
+ }
+ }
+}
+", expectedToCompile: true);
+
+ var result = RunGenerator(compilation);
+
+ // Should have a diagnostic about side effects
+ Assert.NotEmpty(result.Diagnostics);
+ Assert.Contains(result.Diagnostics, d => d.Id == "EFP0004");
+
+ return Verifier.Verify(result.Diagnostics.Select(d => d.ToString()));
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_IncrementOperator_ReportsError()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ var x = 5;
+ x++;
+ return x;
+ }
+ }
+}
+", expectedToCompile: true);
+
+ var result = RunGenerator(compilation);
+
+ // Should have a diagnostic about side effects
+ Assert.NotEmpty(result.Diagnostics);
+ Assert.Contains(result.Diagnostics, d => d.Id == "EFP0004");
+
+ return Verifier.Verify(result.Diagnostics.Select(d => d.ToString()));
+ }
+
+ [Fact]
+ public Task BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ public int Bar { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int Foo()
+ {
+ Console.WriteLine(""test"");
+ return Bar;
+ }
+ }
+}
+", expectedToCompile: true);
+
+ var result = RunGenerator(compilation);
+
+ // Should have a diagnostic about potential side effects
+ Assert.NotEmpty(result.Diagnostics);
+ Assert.Contains(result.Diagnostics, d => d.Id == "EFP0005");
+
+ return Verifier.Verify(result.Diagnostics.Select(d => d.ToString()));
+ }
+
+ [Fact]
+ public Task MethodOverloads_WithDifferentParameterTypes()
+ {
+ // lang=csharp
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ [Projectable]
+ public int Method(int x) => x;
+
+ [Projectable]
+ public int Method(string s) => s.Length;
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Equal(2, result.GeneratedTrees.Length);
+
+ // Verify both overloads are generated with distinct names
+ var generatedFiles = result.GeneratedTrees.Select(t => t.FilePath).ToList();
+ Assert.Contains(generatedFiles, f => f.Contains("Method_P0_int.g.cs"));
+ Assert.Contains(generatedFiles, f => f.Contains("Method_P0_string.g.cs"));
+
+ return Verifier.Verify(result.GeneratedTrees.Select(t => t.ToString()));
+ }
+
+ [Fact]
+ public Task MethodOverloads_WithDifferentParameterCounts()
+ {
+ // lang=csharp
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+namespace Foo {
+ class C {
+ [Projectable]
+ public int Method(int x) => x;
+
+ [Projectable]
+ public int Method(int x, int y) => x + y;
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Equal(2, result.GeneratedTrees.Length);
+
+ // Verify both overloads are generated with distinct names
+ var generatedFiles = result.GeneratedTrees.Select(t => t.FilePath).ToList();
+ Assert.Contains(generatedFiles, f => f.Contains("Method_P0_int.g.cs"));
+ Assert.Contains(generatedFiles, f => f.Contains("Method_P0_int_P1_int.g.cs"));
+
+ return Verifier.Verify(result.GeneratedTrees.Select(t => t.ToString()));
+ }
+
+#if NET10_0_OR_GREATER
+ [Fact]
+ public Task ExtensionMemberProperty()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ class Entity {
+ public int Id { get; set; }
+ }
+
+ static class EntityExtensions {
+ extension(Entity e) {
+ [Projectable]
+ public int DoubleId => e.Id * 2;
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task ExtensionMemberMethod()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ class Entity {
+ public int Id { get; set; }
+ }
+
+ static class EntityExtensions {
+ extension(Entity e) {
+ [Projectable]
+ public int TripleId() => e.Id * 3;
+ }
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task ExtensionMemberMethodWithParameters()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ class Entity {
+ public int Id { get; set; }
+ }
+
+ static class EntityExtensions {
+ extension(Entity e) {
+ [Projectable]
public int Multiply(int factor) => e.Id * factor;
}
}
@@ -2538,6 +3321,58 @@ public record Entity
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
+ [Fact]
+ public void BlockBodiedMethod_WithoutAllowFlag_EmitsWarning()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ class C {
+ public int Value { get; set; }
+
+ [Projectable]
+ public int GetDouble()
+ {
+ return Value * 2;
+ }
+ }
+}
+");
+ var result = RunGenerator(compilation);
+
+ // Should have a warning about experimental feature
+ var diagnostic = Assert.Single(result.Diagnostics);
+ Assert.Equal("EFP0001", diagnostic.Id);
+ Assert.Equal(DiagnosticSeverity.Warning, diagnostic.Severity);
+ }
+
+ [Fact]
+ public void BlockBodiedMethod_WithAllowFlag_NoWarning()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ class C {
+ public int Value { get; set; }
+
+ [Projectable(AllowBlockBody = true)]
+ public int GetDouble()
+ {
+ return Value * 2;
+ }
+ }
+}
+");
+ var result = RunGenerator(compilation);
+
+ // Should have no warnings
+ Assert.Empty(result.Diagnostics);
+ }
+
#region Helpers
Compilation CreateCompilation(string source, bool expectedToCompile = true)