Problem
Headless consumers of OutWit packages that only need serialization / database functionality still run AspectInjector at build time, even when they never use [Notify] (or any other weave target).
We hit this in CdpMcp via:
OutWit.Database.EntityFramework 14.0.1
→ OutWit.Common.MemoryPack 1.1.6
→ OutWit.Common.Aspects 1.3.4
→ AspectInjector 2.8.2
Build log on every project in the graph:
AspectInjector|2.8.2: Found 0 aspects, 0 injections
On macOS CI this is not harmless — AspectInjector is memory-hungry and we had builds Killed: 9 (OOM). Our current workaround is AspectInjector_Enabled=false in Directory.Build.props, which works but feels wrong for a consumer that never opted into aspects.
Root cause
OutWit.Common.Aspects references AspectInjector without PrivateAssets=all:
<PackageReference Include="AspectInjector" />
Published nuspec (1.3.4) lists the dependency with exclude="Build,Analyzers", but does not exclude buildTransitive, so AspectInjector's weave targets still flow to transitive consumers.
OutWit.Common.MemoryPack depends on Aspects via ProjectReference but (as far as we can tell) does not use [Notify] anywhere in its source — so headless consumers inherit a weave they don't need.
Why this is tricky (not a naive one-liner)
I understand the DX motivation: [Notify] requires compile-time IL weaving on each assembly that uses the attribute, not just on OutWit.Common.Aspects.dll itself. Transitive AspectInjector means MVVM consumers can dotnet add package OutWit.Common.Aspects and [Notify] works without a second package — convenient.
I also saw the comment in MVVM/OutWit.Common.MVVM.Navigation/OutWit.Common.MVVM.Navigation.csproj acknowledging that weave reaches the assembly through the package's buildTransitive targets.
So fixing headless leakage must not break MVVM [Notify] DX.
Proposed directions (happy to PR whichever you prefer)
A) PrivateAssets=all on AspectInjector in Aspects (+ DependencyInjection)
<PackageReference Include="AspectInjector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
- Stops AspectInjector from appearing in the packed nuspec dependency graph.
- Breaking for consumers who rely on transitive weave — they would need an explicit
AspectInjector reference, or you ship a small buildTransitive/OutWit.Common.Aspects.targets that pulls it in only for aspect users.
B) Drop OutWit.Common.Aspects from MemoryPack (if truly unused)
Narrowest fix for the Database → MemoryPack → headless path. Worth checking whether MemoryPack only needs something from Common proper (e.g. PropertyChangedEventArgs formatter) without the Aspects project reference.
C) Ship explicit weave propagation only from MVVM-oriented packages
Keep Aspects clean (A), and let OutWit.Common.MVVM.* / similar packages own the buildTransitive targets that enable weave for their consumers — instead of leaking through MemoryPack/Json/MessagePack/etc.
What we can contribute
We've already traced the graph against published NuGet (1.3.4 / 1.1.6 / 14.0.1) and upstream main. Happy to open a PR once you pick a direction — including:
- patch +
dotnet pack verification
- confirmation that CdpMcp builds without
AspectInjector_Enabled=false and without AspectInjector in the log
- whatever tests you want run in Common (Aspects + MVVM)
Environment
OutWit.Database.EntityFramework 14.0.1
OutWit.Common.MemoryPack 1.1.6 (transitive)
OutWit.Common.Aspects 1.3.4 (transitive)
AspectInjector 2.8.2 (transitive, weave runs)
- .NET 10, macOS CI (GitHub Actions)
No rush — just wanted to raise this before we maintain a permanent AspectInjector_Enabled=false workaround upstream of your packages.
Problem
Headless consumers of OutWit packages that only need serialization / database functionality still run AspectInjector at build time, even when they never use
[Notify](or any other weave target).We hit this in CdpMcp via:
Build log on every project in the graph:
On macOS CI this is not harmless — AspectInjector is memory-hungry and we had builds Killed: 9 (OOM). Our current workaround is
AspectInjector_Enabled=falseinDirectory.Build.props, which works but feels wrong for a consumer that never opted into aspects.Root cause
OutWit.Common.Aspectsreferences AspectInjector withoutPrivateAssets=all:Published nuspec (
1.3.4) lists the dependency withexclude="Build,Analyzers", but does not excludebuildTransitive, so AspectInjector's weave targets still flow to transitive consumers.OutWit.Common.MemoryPackdepends on Aspects viaProjectReferencebut (as far as we can tell) does not use[Notify]anywhere in its source — so headless consumers inherit a weave they don't need.Why this is tricky (not a naive one-liner)
I understand the DX motivation:
[Notify]requires compile-time IL weaving on each assembly that uses the attribute, not just onOutWit.Common.Aspects.dllitself. Transitive AspectInjector means MVVM consumers candotnet add package OutWit.Common.Aspectsand[Notify]works without a second package — convenient.I also saw the comment in
MVVM/OutWit.Common.MVVM.Navigation/OutWit.Common.MVVM.Navigation.csprojacknowledging that weave reaches the assembly through the package'sbuildTransitivetargets.So fixing headless leakage must not break MVVM
[Notify]DX.Proposed directions (happy to PR whichever you prefer)
A)
PrivateAssets=allon AspectInjector in Aspects (+ DependencyInjection)AspectInjectorreference, or you ship a smallbuildTransitive/OutWit.Common.Aspects.targetsthat pulls it in only for aspect users.B) Drop
OutWit.Common.Aspectsfrom MemoryPack (if truly unused)Narrowest fix for the Database → MemoryPack → headless path. Worth checking whether MemoryPack only needs something from Common proper (e.g.
PropertyChangedEventArgsformatter) without the Aspects project reference.C) Ship explicit weave propagation only from MVVM-oriented packages
Keep Aspects clean (A), and let
OutWit.Common.MVVM.*/ similar packages own thebuildTransitivetargets that enable weave for their consumers — instead of leaking through MemoryPack/Json/MessagePack/etc.What we can contribute
We've already traced the graph against published NuGet (
1.3.4/1.1.6/14.0.1) and upstreammain. Happy to open a PR once you pick a direction — including:dotnet packverificationAspectInjector_Enabled=falseand without AspectInjector in the logEnvironment
OutWit.Database.EntityFramework14.0.1OutWit.Common.MemoryPack1.1.6 (transitive)OutWit.Common.Aspects1.3.4 (transitive)AspectInjector2.8.2 (transitive, weave runs)No rush — just wanted to raise this before we maintain a permanent
AspectInjector_Enabled=falseworkaround upstream of your packages.