Skip to content

Diagnostics & Code Fixes ​

The Projectables source generator emits diagnostics (warnings and errors) during compilation to help you identify and fix issues with your projectable members. Many diagnostics also have IDE code fixes that resolve them automatically.

Overview ​

IDSeverityTitleCode Fix
EFP0001⚠️ WarningBlock-bodied member support is experimentalAdd AllowBlockBody = true
EFP0002❌ ErrorNull-conditional expression not configuredSet NullConditionalRewriteSupport
EFP0003⚠️ WarningUnsupported statement in block-bodied method—
EFP0004❌ ErrorStatement with side effects in block-bodied method—
EFP0005⚠️ WarningPotential side effect in block-bodied method—
EFP0006❌ ErrorMethod or property requires a body definition—
EFP0007❌ ErrorUnsupported pattern in projectable expression—
EFP0008❌ ErrorTarget class is missing a parameterless constructorAdd parameterless constructor
EFP0009❌ ErrorDelegated constructor cannot be analyzed for projection—
EFP0010❌ ErrorUseMemberBody target member not found—
EFP0011❌ ErrorUseMemberBody target member is incompatible—
EFP0012ℹ️ Info[Projectable] factory method can be converted to a constructorConvert to constructor

EFP0001 — Block-bodied member support is experimental ​

Severity: Warning
Category: Design

Message ​

Block-bodied member '{0}' is using an experimental feature. 
Set AllowBlockBody = true on the Projectable attribute to suppress this warning.

Cause ​

A [Projectable] member uses a block body ({ ... }) instead of an expression body (=>), which is an experimental feature.

Fix ​

The IDE offers a quick-fix to add AllowBlockBody = true automatically. You can also apply it manually:

csharp
// Before (warning)
[Projectable]
public string GetCategory()
{
    if (Value > 100) return "High";
    return "Low";
}

// After (warning suppressed)
[Projectable(AllowBlockBody = true)]
public string GetCategory()
{
    if (Value > 100) return "High";
    return "Low";
}

Or convert to an expression-bodied member:

csharp
[Projectable]
public string GetCategory() => Value > 100 ? "High" : "Low";

EFP0002 — Null-conditional expression not configured ​

Severity: Error
Category: Design

Message ​

'{0}' has a null-conditional expression exposed but is not configured to rewrite this 
(Consider configuring a strategy using the NullConditionalRewriteSupport property 
on the Projectable attribute)

Cause ​

The projectable member's body contains a null-conditional operator (?.) but NullConditionalRewriteSupport is not configured (defaults to None).

Fix ​

The IDE offers two code-fix options — Ignore and Rewrite — which set the attribute property automatically:

csharp
// ❌ Error
[Projectable]
public string? FullAddress => Location?.AddressLine1 + " " + Location?.City;

// ✅ Option 1: Ignore (strips the ?. — safe for SQL Server)
[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
public string? FullAddress => Location?.AddressLine1 + " " + Location?.City;

// ✅ Option 2: Rewrite (explicit null checks)
[Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
public string? FullAddress => Location?.AddressLine1 + " " + Location?.City;

// ✅ Option 3: Rewrite the expression manually
[Projectable]
public string? FullAddress =>
    Location != null ? Location.AddressLine1 + " " + Location.City : null;

See Null-Conditional Rewrite for details.


EFP0003 — Unsupported statement in block-bodied method ​

Severity: Warning
Category: Design

Message ​

Method '{0}' contains an unsupported statement: {1}

Cause ​

A block-bodied [Projectable] member contains a statement type that cannot be converted to an expression tree (e.g., loops, try-catch, throw, new object instantiation in statement position).

Unsupported Statements ​

  • while, for, foreach loops
  • try/catch/finally blocks
  • throw statements
  • Object instantiation as a statement (not in a return)

Fix ​

Refactor to use only supported constructs (if/else, switch, local variables, return), or convert to an expression-bodied member:

csharp
// ❌ Warning: loops are not supported
[Projectable(AllowBlockBody = true)]
public int SumItems()
{
    int total = 0;
    foreach (var item in Items)  // EFP0003
        total += item.Price;
    return total;
}

// ✅ Use LINQ instead
[Projectable]
public int SumItems() => Items.Sum(i => i.Price);

EFP0004 — Statement with side effects in block-bodied method ​

Severity: Error
Category: Design

Message ​

Context-specific — one of:

  • Property assignment '{0}' has side effects and cannot be used in projectable methods
  • Compound assignment operator '{0}' has side effects
  • Increment/decrement operator '{0}' has side effects

Cause ​

A block-bodied projectable member modifies state. Expression trees cannot represent side effects.

Triggers ​

csharp
// ❌ Property assignment
Bar = 10;

// ❌ Compound assignment
Bar += 10;

// ❌ Increment / Decrement
Bar++;
--Count;

Fix ​

Remove the side-effecting statement. Projectable members must be pure functions — they can only read data and return a value.

csharp
// ❌ Error: has side effects
[Projectable(AllowBlockBody = true)]
public int Foo()
{
    Bar = 10;       // EFP0004
    return Bar;
}

// ✅ Read-only computation
[Projectable]
public int Foo() => Bar + 10;

EFP0005 — Potential side effect in block-bodied method ​

Severity: Warning
Category: Design

Message ​

Method call '{0}' may have side effects and cannot be guaranteed to be safe in projectable methods

Cause ​

A block-bodied projectable member calls a method that is not itself marked with [Projectable]. Such calls may have side effects that cannot be represented in an expression tree.

Example ​

csharp
[Projectable(AllowBlockBody = true)]
public int Foo()
{
    Console.WriteLine("test");  // ⚠️ EFP0005 — may have side effects
    return Bar;
}

Fix ​

  • Remove the method call if it is not needed in a query context.
  • If the method is safe to use in queries, mark it with [Projectable].

EFP0006 — Method or property requires a body definition ​

Severity: Error
Category: Design

Message ​

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.

Cause ​

A [Projectable] member has no body — it is abstract, an interface declaration, or an auto-property without an expression.

Fix ​

Provide a body, or use UseMemberBody to delegate to another member:

csharp
// ❌ Error: no body
[Projectable]
public string FullName { get; set; }

// ✅ Expression-bodied property
[Projectable]
public string FullName => FirstName + " " + LastName;

// ✅ Delegate to another member
[Projectable(UseMemberBody = nameof(ComputeFullName))]
public string FullName => ComputeFullName();
private string ComputeFullName() => FirstName + " " + LastName;

EFP0007 — Unsupported pattern in projectable expression ​

Severity: Error
Category: Design

Message ​

The pattern '{0}' cannot be rewritten into an expression tree.
Simplify the pattern or restructure the projectable member body.

Cause ​

A pattern used inside a [Projectable] member (e.g. in a switch expression or an is expression) cannot be translated into an expression tree. Unsupported patterns include positional/deconstruct patterns and variable designations outside switch arms.

Supported Patterns ​

PatternExample
Constant1 => "one"
Discard / default_ => "other"
TypeGroupItem g => …
Relational>= 90 => "A"
and / or combined>= 80 and < 90 => "B"
when guard4 when Prop == 12 => …
Propertyentity is { IsActive: true }
not null / notName is not null

Fix ​

Rewrite using a supported pattern or convert to an explicit conditional expression:

csharp
// ❌ Error — positional pattern not supported
[Projectable]
public bool IsOrigin(Point p) => p is (0, 0);

// ✅ Use a property pattern instead
[Projectable]
public bool IsOrigin(Point p) => p is { X: 0, Y: 0 };

See also: Pattern Matching.


EFP0008 — Target class is missing a parameterless constructor ​

Severity: Error
Category: Design

Message ​

Class '{0}' must have a parameterless constructor to be used with a [Projectable] constructor.
The generated projection uses 'new {0}() { ... }' (object-initializer syntax),
which requires an accessible parameterless constructor.

Cause ​

A constructor is marked [Projectable], but the class does not expose a public, internal, or protected-internal parameterless constructor. The generator emits new T() { … } syntax which requires one.

Fix ​

The IDE inserts the parameterless constructor automatically. You can also add it manually:

csharp
public class CustomerDto
{
    public CustomerDto() { }   // ← inserted by code fix

    [Projectable]
    public CustomerDto(Customer customer) { … }
}

See also: Constructor Projections.


EFP0009 — Delegated constructor cannot be analyzed for projection ​

Severity: Error
Category: Design

Message ​

The delegated constructor '{0}' in type '{1}' has no source available and cannot be analyzed.
Base/this initializer in member '{2}' will not be projected.

Cause ​

A [Projectable] constructor delegates to another constructor via : base(…) or : this(…), but the target constructor's source code is not available in the current compilation (e.g. it lives in a referenced binary).

Fix ​

Ensure the delegated constructor's source is available (e.g. move it to the same project), or restructure to avoid the delegation.


EFP0010 — UseMemberBody target member not found ​

Severity: Error
Category: Design

Message ​

Member '{1}' referenced by UseMemberBody on '{0}' was not found on type '{2}'

Cause ​

The name passed to UseMemberBody does not match any member on the containing type.

Fix ​

Verify the member name — use nameof(...) to avoid typos:

csharp
// ❌ Error — "ComputeFullname" (lowercase n) doesn't exist
[Projectable(UseMemberBody = "ComputeFullname")]
public string FullName => …;

// ✅ Use nameof to catch mistakes at compile time
[Projectable(UseMemberBody = nameof(ComputeFullName))]
public string FullName => …;
private string ComputeFullName => FirstName + " " + LastName;

See also: Use Member Body.


EFP0011 — UseMemberBody target member is incompatible ​

Severity: Error
Category: Design

Message ​

Member '{1}' referenced by UseMemberBody on '{0}' has an incompatible type or signature

Cause ​

A member with the given name exists on the type but its return type or parameter list is incompatible with the [Projectable] member.

Fix ​

Align the target member's signature with the projectable member. For Expression<Func<…>> properties the lambda parameter types must correspond to the projectable's parameters:

csharp
// ❌ Error — return type mismatch (string vs int)
[Projectable(UseMemberBody = nameof(ComputeExpr))]
public int Computed => …;
private static Expression<Func<Entity, string>> ComputeExpr => @this => @this.Name;

// ✅ Matching return type
private static Expression<Func<Entity, int>> ComputeExpr => @this => @this.Id * 2;

See also: Use Member Body.


EFP0012 — [Projectable] factory method can be converted to a constructor ​

Severity: Info
Category: Design

Message ​

Factory method '{0}' creates and returns an instance of the containing class via object initializer.
Consider converting it to a [Projectable] constructor.

Cause ​

A [Projectable] static or instance method returns a new T { … } object initializer where T is the containing class. This is equivalent to a [Projectable] constructor and the IDE can convert it automatically.

Fix ​

Two IDE actions are available — as a quick-fix on the diagnostic, or as a refactoring (always available even when the diagnostic is suppressed via the IDE's refactoring menu):

ActionScope
Convert to constructorCurrent document only
Convert to constructor (and update callers)Entire solution
csharp
// Before — factory method
public class CustomerDto
{
    [Projectable]
    public static CustomerDto FromCustomer(Customer c) => new CustomerDto
    {
        Id = c.Id,
        Name = c.FirstName + " " + c.LastName
    };
}

// After — projectable constructor (converted by IDE)
public class CustomerDto
{
    public CustomerDto() { }

    [Projectable]
    public CustomerDto(Customer c)
    {
        Id = c.Id;
        Name = c.FirstName + " " + c.LastName;
    }
}

See also: Constructor Projections.


Suppressing Diagnostics ​

Individual warnings can be suppressed with standard C# pragma directives:

csharp
#pragma warning disable EFP0001
[Projectable]
public string GetValue()
{
    if (IsActive) return "Active";
    return "Inactive";
}
#pragma warning restore EFP0001

Or via .editorconfig / Directory.Build.props:

xml
<PropertyGroup>
  <NoWarn>$(NoWarn);EFP0001;EFP0003</NoWarn>
</PropertyGroup>

Released under the MIT License.