DependencyMagic 0.1.0

dotnet add package DependencyMagic --version 0.1.0
                    
NuGet\Install-Package DependencyMagic -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DependencyMagic" Version="0.1.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DependencyMagic" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="DependencyMagic">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DependencyMagic --version 0.1.0
                    
#r "nuget: DependencyMagic, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DependencyMagic@0.1.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DependencyMagic&version=0.1.0
                    
Install as a Cake Addin
#tool nuget:?package=DependencyMagic&version=0.1.0
                    
Install as a Cake Tool

Dependency Magic

Dependency Magic embeds a consuming project's dependencies as resources in its DLL. It was developed to facilitate consuming NuGet packages in incremental source generators. It may also be useful for distributing single-file executables, although .NET Core can do that on its own now.

How To Use It

Consumers must use C# 9 or higher. Then just add a NuGet dependency on Dependency Magic.

How It Works

Dependency Magic is a source-only NuGet package. If you're building it from source you can't consume it as a DLL or project reference. You have to pack it and install it in a local NuGet feed.

There are only four moving parts:

  • A build target that embeds direct and transitive dependencies in the consumer's DLL.
  • A handler for AppDomain.AssemblyResolve that registers itself via a [ModuleInitializer].
  • A polyfill implementation of ModuleInitializerAttribute.
  • A properties file that hides all these things in your IDE.

The C# types are compiled as part of the consuming project. This resolves the circular problem of a source generator resolving a dependency on a Dependency Magic DLL. It also makes it much simpler for the assembly resolver to identify the assembly containing the embedded resources.

Caveats

If you're publishing an incremental source generator you should probably only use Dependency Magic during development. As I understand it, all ISGs contributing to a build run in the same AppDomain. Therefore, if two ISGs depend on different versions of the same assembly, only one version of the assembly can be loaded and chaos may ensue. Dependency Magic can't help with that. It just makes it easy for ISGs to consume dependencies when there are no conflicts. Of course, you never know what other ISGs the consumer of your ISG might be using. So unless and until MS allows us to run each ISG in its own AppDomain, the general solution is to inline all dependencies as source. (Or go back to the old console app source generators, which ran in their own process and performed just fine.)

Why do all these System libraries get embedded?

We don't need to embed transitive dependencies that are available in the runtime. But MSBuild doesn't know what runtime environment a .NET Standard library might run in, and there are a few System and Microsoft packages that are available in the .NET Core runtime but not in .NET Framework or vice versa.

Ugly Details

When MSBuild resolves dependencies it puts their metadata in an item list called @(ReferencePath). The metadata is slightly different depending on the target framework. For netstandard2.0, the NuGetPackageId of framework libraries is NETStandard.Library. For net8.0, it's Microsoft.NETCore.App.Ref. And for net48, framework libraries are identified by FrameworkFile='true'.

If the CopyLocalLockFileAssemblies property is true, MSBuild creates another item list called @(ReferenceCopyLocalPaths). This loses the aforementioned framework metadata, but I think MSBuild filters the copy local assemblies in a similar way. All I can say for sure is that when targeting netstandard2.0 with some arbitrary NuGet dependencies, exactly the same transitive dependencies that came from NETStandard.Library were excluded from @(ReferenceCopyLocalPaths). I didn't compare other frameworks.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0 486 8/27/2025