Skip to content

AspectInjector leaks to headless transitive consumers (MemoryPack/Database path) #1

Description

@KarataevDmitry

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions