Skip to content

NativeAOT support: DynamicallyAccessedMembers annotations + TUnit test migration#4

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/add-native-aot-support
Draft

NativeAOT support: DynamicallyAccessedMembers annotations + TUnit test migration#4
Copilot wants to merge 3 commits intomainfrom
copilot/add-native-aot-support

Conversation

Copy link

Copilot AI commented Feb 26, 2026

Adds NativeAOT compatibility across all library projects and migrates the test project to TUnit (which supports NativeAOT via compile-time test discovery).

Core AOT fix: Activator.CreateInstanceArray.CreateInstance

The IEnumerable<T> resolution path used Activator.CreateInstance(typeof(List<>).MakeGenericType(T)) — a runtime generic instantiation that NativeAOT cannot compile. Replaced with Array.CreateInstance(T, count), which returns a covariant T[] satisfying IEnumerable<T> without JIT.

Precise AOT annotations

Rather than blanket [RequiresDynamicCode] + [RequiresUnreferencedCode] on all resolve methods, annotations are now scoped to what each method actually does:

[DynamicallyAccessedMembers(PublicConstructors)] — on DependencyRelation.ImplementType and all Type implementType parameters down the private resolve chain (ResolveTypeToObjectResolveSingleton/Transient/ScopedResolveDescriptionWithImplementTypeResolveParameterInfos). Tells the trimmer exactly which members to preserve for registered implementation types.

// DependencyRelation.cs
public record DependencyRelation(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
    Type ImplementType, ...);

// Depository.Resolve.cs
private object ResolveTypeToObject(
    [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type implementType, ...)
{
    var constructorInfos = implementType.GetConstructors(); // trimmer now knows to preserve these
    ...
}

[RequiresDynamicCode] — retained only where MakeGenericType/MakeGenericMethod is genuinely called:

  • ResolveDependency / ResolveDependencies (open-generic and Task<> paths)
  • ResolveGenericDependency / ResolveGenericDependencies
  • PostTypeChangeNotification / NotifyDependencyChange (notification dispatch constructs IEnumerable<T> and INotifyDependencyChanged<T> at runtime)
  • Relation mutation methods (AddRelation, DeleteRelation, DisableRelation, EnableRelation, ChangeFocusingRelation) and their extension method wrappers — all propagate through to NotifyDependencyChange

[RequiresUnreferencedCode] removed from ResolveDependencies, ResolveDependency, ResolveParameterInfos — DAM now handles member preservation.

[UnconditionalSuppressMessage] with justification on SafeToString and GetParameterServiceKey, where reflection on a runtime GetType() result is intentional and can't be statically annotated.

Annotation consistency across the full chain

All annotations are propagated to interfaces (IDepositoryResolve, IRelationDepository, INotificationHub) and all call sites (NotificationHub, DepositoryServiceProvider, DepositoryServiceProviderFactory, extension methods).

AOT polyfills

DynamicallyAccessedMembersAttribute, DynamicallyAccessedMemberTypes, UnconditionalSuppressMessageAttribute, and RequiresDynamicCodeAttribute polyfills added for netstandard2.0 targets in both Depository.Abstraction and Depository.Core. Depository.Extensions gets a minimal RequiresDynamicCode-only polyfill; Depository.Extensions.DependencyInjection inherits Core's polyfills via the existing InternalsVisibleTo.

Library project AOT markers

All four library projects (Depository.Abstraction, Depository.Core, Depository.Extensions, Depository.Extensions.DependencyInjection) declare:

<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">true</IsAotCompatible>

Test project migration to TUnit

Depository.Tests migrated from xUnit to TUnit 1.17.25, which uses source generators for test discovery (NativeAOT-compatible). Key changes: [Fact][Test], async voidasync Task, Assert.IsAssignableFrom<T> replaced with direct casts, <OutputType>Exe</OutputType> + <TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport> added. CI workflow updated to use dotnet run (TUnit's runner) instead of dotnet test.

Original prompt

This section details on the original issue you should resolve

<issue_title>NativeAOT Support</issue_title>
<issue_description>This should support native aot, for ALL project and add nativaaot test support for .Test Project

For some additioinal important point:

  • Migrate the .Test project using TUnit and .NET 9 which support NativeAOT
  • Focus on Activator.CreateInstance in List Generic creator, migrate it carefully</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Co-authored-by: kengwang <30862240+kengwang@users.noreply.github.com>
Copilot AI changed the title [WIP] Add NativeAOT support for all projects and tests Add NativeAOT support and migrate tests to TUnit Feb 26, 2026
…nnotations

Co-authored-by: kengwang <30862240+kengwang@users.noreply.github.com>
Copilot AI changed the title Add NativeAOT support and migrate tests to TUnit NativeAOT support: DynamicallyAccessedMembers annotations + TUnit test migration Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NativeAOT Support

2 participants