Skip to content

NDLRCOR003: [DeferToContainer] attribute in generated code is ignored

Cause

A [DeferToContainer] attribute was found in generated code (a .g.cs, .generated.cs, or .designer.cs file, or in an obj/*/generated/ folder).

Rule Description

Source generators in Roslyn run in parallel and in isolation. Each generator only sees:

  1. Original user-written source code
  2. Referenced assemblies (metadata)

Generators cannot see output from other generators in the same compilation. This means if one generator (like CacheProviderGenerator) adds [DeferToContainer] to a generated partial class, Needlr's TypeRegistryGenerator will never see it.

Compilation Pipeline:

     Original Source Files
     ┌─────┴─────┐
     │           │
     ▼           ▼
[CacheProvider  [TypeRegistry
 Generator]      Generator]
     │           │
     ▼           ▼
  Output A     Output B    ◄── These can't see each other!
     │           │
     └─────┬─────┘
    Final Compilation
      [Analyzers]  ◄── CAN see everything (this is how we detect the issue)

When TypeRegistryGenerator runs, it only sees your original partial class (without the attribute) and generates incorrect factory code with a parameterless constructor call.

How to Fix

Move the [DeferToContainer] attribute to your original source file - the partial class declaration you wrote, not the one generated.

Before (broken)

// Your code (MyService.cs)
[CacheProvider("EngageFeed")]  // This triggers CacheProviderGenerator
public partial class EngageFeedCacheProvider { }

// Generated by CacheProviderGenerator (EngageFeedCacheProvider.g.cs)
[DeferToContainer(typeof(ICacheProvider))]  // ❌ TypeRegistryGenerator won't see this!
public sealed partial class EngageFeedCacheProvider(ICacheProvider _cacheProvider)
{
    // ...
}

After (correct)

// Your code (MyService.cs)
[DeferToContainer(typeof(ICacheProvider))]  // ✅ TypeRegistryGenerator sees this!
[CacheProvider("EngageFeed")]
public partial class EngageFeedCacheProvider { }

// Generated by CacheProviderGenerator (EngageFeedCacheProvider.g.cs)
public sealed partial class EngageFeedCacheProvider(ICacheProvider _cacheProvider)
{
    // ...
}

Impact if Ignored

If you ignore this error, Needlr will generate factory code like:

sp => new EngageFeedCacheProvider()  // Missing the constructor parameter!

This will cause a compile error:

CS7036: There is no argument given that corresponds to the required parameter 
'_cacheProvider' of 'EngageFeedCacheProvider.EngageFeedCacheProvider(ICacheProvider)'

For Generator Authors

If you're writing a source generator that adds constructors to partial classes, do not add [DeferToContainer] to your generated output. Instead, document that users must add the attribute to their original partial class.

Example documentation for your generator's users:

## Usage with Needlr

If using Needlr for dependency injection, add the `[DeferToContainer]` attribute 
to your original class declaration:

```csharp
[DeferToContainer(typeof(ICacheProvider))]
[CacheProvider("EngageFeed")]
public partial class EngageFeedCacheProvider { }

This tells Needlr's source generator what constructor parameters to expect.

## When to Suppress

This diagnostic should generally **not be suppressed**. The attribute in generated code will not work as intended.

If you must suppress it (for example, during migration), be aware that you'll need to manually register the service:

```csharp
#pragma warning disable NDLRCOR003
// In generated file - this won't work but we're registering manually
#pragma warning restore NDLRCOR003

// In your Program.cs or startup:
builder.Services.AddSingleton<EngageFeedCacheProvider>(sp => 
    new EngageFeedCacheProvider(sp.GetRequiredService<ICacheProvider>()));

See Also