NDLRGEN020: [Options] is not compatible with AOT¶
Cause¶
A class has the [Options] attribute but is in a project with PublishAot=true or IsAotCompatible=true.
Rule Description¶
The [Options] attribute generates code that calls Microsoft's configuration binding APIs:
Configure<T>()BindConfiguration()ValidateDataAnnotations()
These APIs use reflection to bind configuration values to properties at runtime. They are marked with [RequiresDynamicCode] and [RequiresUnreferencedCode], making them incompatible with:
- Native AOT - No JIT compiler to generate code at runtime
- Trimming - Reflection targets may be trimmed away
How to Fix¶
You have two options:
Option 1: Remove [Options] from AOT Projects¶
If you're building a plugin or library that must be AOT-compatible, don't use [Options]:
// ❌ Won't work in AOT
[Options]
public class CacheSettings
{
public int TimeoutSeconds { get; set; }
}
// ✅ Manual configuration binding
public class CacheSettings
{
public int TimeoutSeconds { get; set; }
public static CacheSettings FromConfiguration(IConfiguration config)
{
return new CacheSettings
{
TimeoutSeconds = config.GetValue<int>("Cache:TimeoutSeconds")
};
}
}
Option 2: Disable AOT for the Project¶
If you need [Options] functionality, disable AOT:
<PropertyGroup>
<!-- Remove or set to false -->
<PublishAot>false</PublishAot>
<IsAotCompatible>false</IsAotCompatible>
</PropertyGroup>
Example¶
Code with Error¶
using NexusLabs.Needlr.Generators;
// In a project with <PublishAot>true</PublishAot>
// NDLRGEN020: Type 'DatabaseOptions' has [Options] attribute but is in an
// AOT-enabled project
[Options]
public class DatabaseOptions
{
public string ConnectionString { get; set; } = "";
}
Fixed Code (Manual Binding)¶
// Remove [Options] and bind manually
public class DatabaseOptions
{
public string ConnectionString { get; set; } = "";
}
// In your startup code:
public static class ServiceRegistration
{
public static void RegisterOptions(IServiceCollection services, IConfiguration config)
{
var dbOptions = new DatabaseOptions
{
ConnectionString = config["Database:ConnectionString"] ?? ""
};
services.AddSingleton(Microsoft.Extensions.Options.Options.Create(dbOptions));
}
}
Why This Limitation Exists¶
Microsoft's Configuration Binding Generator (<EnableConfigurationBindingGenerator>) uses C# interceptors to replace reflection-based calls with compile-time generated code. However:
- Interceptors can only intercept calls in hand-written source code
- Needlr's
[Options]generates theConfigure<T>()calls inTypeRegistry.g.cs - One source generator cannot intercept another's output
Therefore, the reflection-based calls cannot be replaced with AOT-safe alternatives.
Future Considerations¶
A future version of Needlr may generate AOT-compatible binding code directly, similar to Microsoft's approach. Until then, manual configuration binding is required for AOT projects.
Affected Project Properties¶
This error is triggered when either of these MSBuild properties is true:
| Property | Purpose |
|---|---|
PublishAot |
Enables Native AOT publishing |
IsAotCompatible |
Marks a library as AOT-compatible |