Design an explicit Native AOT-compatible model mapping path
Problem
We would like to use MaxMind.Db (and, transitively, MaxMind.GeoIP2) from
.NET Native AOT applications.
Enabling the AOT and trimming analyzers currently reports warnings in the
reader/deserializer path. A native-published smoke application can also fail
while constructing Metadata, because its non-public constructor is discovered
through reflection and may be removed by trimming.
A minimal preservation fix for Metadata is small and will be proposed
separately. It fixes that concrete runtime failure, but it does not make the
package fully AOT-compatible: the general Find<T> path still builds
deserialization metadata at runtime using reflection, including closed generic
construction.
Goal
Provide a public, statically analyzable database lookup path that:
- does not require runtime code generation;
- does not rely on unbounded reflection over user model types;
- preserves the existing mapping behavior (
MapKey, nested records/classes,
collections, nullable values, injected values, and network data);
- can be exercised by a real
dotnet publish -p:PublishAot=true smoke test; and
- eventually allows the package to set
<IsAotCompatible>true</IsAotCompatible>
without suppressing analyzer warnings.
The existing reflection-based API can remain for compatibility and be
annotated honestly if necessary.
Proposed direction
Introduce an explicit descriptor/type-info abstraction for mapped models. The
descriptor would contain the object factory and member mappings needed by the
decoder. It could be:
- supplied manually by a consumer;
- generated at compile time by a source generator from the existing mapping
attributes; and/or
- shipped by higher-level packages such as
MaxMind.GeoIP2 for their built-in
response models.
Conceptually, the reader would gain an overload along these lines (names only
illustrative):
T? Find<T>(IPAddress address, IMaxMindDbTypeInfo<T> typeInfo);
The decoder would consume this descriptor instead of calling
Type.GetTypeInfo(), GetCustomAttributes(), MakeGenericType(), and reflected
constructors/setters for every model graph. The current Find<T>(...) overload
could keep using the reflection-backed descriptor builder.
This separates two concerns:
- the binary decoder, which can be AOT-safe; and
- model metadata discovery, which is currently reflection-based but can be
moved to build time.
Suggested delivery sequence
- Preserve the internal
Metadata constructor and add a Native AOT smoke test
(small correctness PR).
- Agree on the descriptor API and supported mapping semantics in this issue.
- Implement a descriptor-driven decoder path with tests matching the
reflection path.
- Add source generation or generated descriptors for first-party GeoIP2
models.
- Enable AOT compatibility metadata only after analyzer-clean build and
native-publish/runtime tests.
Related GeoIP2 constraint
MaxMind.GeoIP2 also has a separate web-service concern: its reflection-based
System.Text.Json calls produce trimming/AOT warnings. A straightforward
source-generated context is not behavior-preserving today because the public
response records use init properties with non-null default initializers;
missing JSON members can become explicit null constructor arguments in
generated code. That should be handled independently (for example, internal
mutable wire DTOs plus mapping) rather than hidden with warning suppressions.
Would this descriptor/source-generation direction fit the project's API
design? If so, I can turn the proposal into a smaller API sketch and
implementation PR before changing the existing reader surface.
Design an explicit Native AOT-compatible model mapping path
Problem
We would like to use
MaxMind.Db(and, transitively,MaxMind.GeoIP2) from.NET Native AOT applications.
Enabling the AOT and trimming analyzers currently reports warnings in the
reader/deserializer path. A native-published smoke application can also fail
while constructing
Metadata, because its non-public constructor is discoveredthrough reflection and may be removed by trimming.
A minimal preservation fix for
Metadatais small and will be proposedseparately. It fixes that concrete runtime failure, but it does not make the
package fully AOT-compatible: the general
Find<T>path still buildsdeserialization metadata at runtime using reflection, including closed generic
construction.
Goal
Provide a public, statically analyzable database lookup path that:
MapKey, nested records/classes,collections, nullable values, injected values, and network data);
dotnet publish -p:PublishAot=truesmoke test; and<IsAotCompatible>true</IsAotCompatible>without suppressing analyzer warnings.
The existing reflection-based API can remain for compatibility and be
annotated honestly if necessary.
Proposed direction
Introduce an explicit descriptor/type-info abstraction for mapped models. The
descriptor would contain the object factory and member mappings needed by the
decoder. It could be:
attributes; and/or
MaxMind.GeoIP2for their built-inresponse models.
Conceptually, the reader would gain an overload along these lines (names only
illustrative):
The decoder would consume this descriptor instead of calling
Type.GetTypeInfo(),GetCustomAttributes(),MakeGenericType(), and reflectedconstructors/setters for every model graph. The current
Find<T>(...)overloadcould keep using the reflection-backed descriptor builder.
This separates two concerns:
moved to build time.
Suggested delivery sequence
Metadataconstructor and add a Native AOT smoke test(small correctness PR).
reflection path.
models.
native-publish/runtime tests.
Related GeoIP2 constraint
MaxMind.GeoIP2also has a separate web-service concern: its reflection-basedSystem.Text.Jsoncalls produce trimming/AOT warnings. A straightforwardsource-generated context is not behavior-preserving today because the public
response records use
initproperties with non-null default initializers;missing JSON members can become explicit
nullconstructor arguments ingenerated code. That should be handled independently (for example, internal
mutable wire DTOs plus mapping) rather than hidden with warning suppressions.
Would this descriptor/source-generation direction fit the project's API
design? If so, I can turn the proposal into a smaller API sketch and
implementation PR before changing the existing reader surface.