SourceCrafter.DependencyInjection
2.26.286.181
dotnet add package SourceCrafter.DependencyInjection --version 2.26.286.181
NuGet\Install-Package SourceCrafter.DependencyInjection -Version 2.26.286.181
<PackageReference Include="SourceCrafter.DependencyInjection" Version="2.26.286.181" />
<PackageVersion Include="SourceCrafter.DependencyInjection" Version="2.26.286.181" />
<PackageReference Include="SourceCrafter.DependencyInjection" />
paket add SourceCrafter.DependencyInjection --version 2.26.286.181
#r "nuget: SourceCrafter.DependencyInjection, 2.26.286.181"
#:package SourceCrafter.DependencyInjection@2.26.286.181
#addin nuget:?package=SourceCrafter.DependencyInjection&version=2.26.286.181
#tool nuget:?package=SourceCrafter.DependencyInjection&version=2.26.286.181
SourceCrafter.DependencyInjection
Truly compile-time dependency injection for .NET. Services are declared with attributes and the Roslyn generator emits plain, readable C# resolvers — no reflection, no expression trees, no runtime container.
[ServiceProvider]
[Singleton<IClock, SystemClock>]
[Scoped<DbSession>]
[Transient<Handler>]
public partial class AppContainer { }
using var container = new AppContainer();
var handler = container.CreateScope().Handler;
Members are strongly typed by default, so an unresolvable service is a compile error. Opt
into the generic IServiceProvider surface with [ServiceProvider(genericApi: true)] when
the container has to be handed to code written against IServiceProvider: it emits real
GetService<T>() / GetRequiredService<T>() members, keyed and async variants included,
that dispatch by runtime type.
Full documentation lives in SourceCrafter.DependencyInjection/README.md —
attribute reference, generated code walkthrough, cancellation semantics and the SCDI
diagnostic table.
Packages
| Package | What it is |
|---|---|
SourceCrafter.DependencyInjection |
The generator. This is the one you usually want. |
SourceCrafter.DependencyInjection.Metadata |
Attributes only, no generator. For libraries that want to declare contracts without pulling in the generator. |
SourceCrafter.DependencyInjection.MsConfiguration |
The generator plus Microsoft.Extensions.Configuration support ([JsonSetting<T>]). Use instead of the core generator. |
SourceCrafter.DependencyInjection.MsConfiguration.Metadata |
Attributes only, configuration flavour. |
Repository layout
SourceCrafter.DependencyInjection/— the generator (netstandard2.0 is deliberately not targeted)SourceCrafter.DependencyInjection.Metadata/— attribute definitionsSourceCrafter.DependencyInjection.MsConfiguration*/— configuration-aware variantsSourceCrafter.DependencyInjection.Tests/— the test suiteBenchmarks/— BenchmarkDotNet harness
Building and testing
dotnet build SourceCrafter.DependencyInjection.slnx
The test project targets .NET 10 and runs through the xUnit v3 in-process runner. dotnet test
is currently unreliable with the .NET 10 SDK here, so run the produced executable directly:
dotnet build SourceCrafter.DependencyInjection.Tests/SourceCrafter.DependencyInjection.Tests.csproj
SourceCrafter.DependencyInjection.Tests/bin/Debug/net10.0/SourceCrafter.DependencyInjection.Tests.exe
Filter with -method "*SomePattern*".
Test layers
Changing the generator means keeping five different kinds of test honest:
Tests.cs/Server.cs— behaviour. A realistic container is compiled by the real generator during the build, then exercised at runtime.GeneratedCodeTests.cs— emission. Runs the generator in memory viaGeneratorHarnessand asserts on the text it produces: lock scoping, the constructor, disposal ordering, file shape, and determinism across repeated passes.RootPropagationTests.cs— theRoot/CreateScopevirtual-dispatch contract.LockingStrategyTests.cs— the locking scheme itself, modelled by hand with no generator involved. Shows why one lock per lifetime deadlocks on cross-lifetime dependencies, why a single lock is correct but serializing, and why an instance lock cannot guard a static field.EqualityContractTests.cs— guards every generator model type against declaringEqualswithout a matching==, which silently breaks incremental caching.
If you touch the emitter, GeneratedCodeTests is where a regression will surface first.
Inspecting generated output
dotnet build -p:EmitCompilerGeneratedFiles=true -p:CompilerGeneratedFilesOutputPath=obj/generated
Emit to a directory outside the project folder, or the generated files get picked up by the next compilation and every type ends up declared twice.
Benchmarks
Benchmarks/ compares this generator against every actively maintained compile-time DI
container — Jab,
Pure.DI,
StrongInject and
MrMeeseeks.DIE — following the scenarios that
.NET Matrix has made the de facto standard for
this category. Results are in the package README.
dotnet run -c Release --project Benchmarks -- --filter "*"
Two rules the harness must keep:
- Every branch does exactly the same work. An earlier version of this harness only built the container in the SourceCrafter branch while the others created a scope and resolved a service, so the published numbers compared nothing.
Hand Codedis the baseline, not another container. Nestednew(...)is the floor a compile-time container is trying to reach.
And one rule for reading the output:
- Do not publish a timing ratio from a single run. These scenarios resolve in tens of
nanoseconds, where per-process JIT and heap-layout luck is a large fraction of the
measurement. A single-launch run once showed all three containers jumping from ~38 ns to
~80 ns while the baseline held still — three independent libraries do not regress in
lockstep.
HarnessConfigtherefore pins three process launches plus memory randomization, and even then the complex-graph scenario has been seen to vary 0.95x–2.10x across runs of the same binary with byte-identical allocations. Allocation counts are reproducible; sub-100 ns timings are only approximately so.
Where a library genuinely lacks a feature (StrongInject and MrMeeseeks.DIE have no request scope), the row is left empty rather than approximated: a made-up equivalent would measure something no user of that library could write.
Each container declaration lives in its own Containers.*.cs file, because the libraries ship
attributes with colliding names ([Transient<,>], [Singleton<,>], [Scoped<,>],
[Register]).
Contributing
Issues and pull requests are welcome. A few conventions that are not obvious from the code:
- The generator project sets
EnableDefaultCompileItems=false. Any new.csfile must be added to the.csprojby hand, or it will silently not compile. - Parsing and emission are separated on purpose. Anything reachable from a cached delegate
must be free of
ISymbol,SyntaxNode,SemanticModelandCompilation, otherwise the incremental pipeline keeps whole Roslyn compilations alive.ResolverRendereris the frozen, symbol-free projection that the emitter renders from. - New diagnostics go in
ServiceContainerDiagnostics.cswith a fresh, uniqueSCDIid, and aDiagnosticDescriptor.Titlethat is static text (dynamic titles break IDE grouping). - Generated code is normalized to tabs, CRLF and no trailing whitespace before it is emitted.
License
See LICENSE.txt.
Learn more about Target Frameworks and .NET Standard.
-
net10.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on SourceCrafter.DependencyInjection:
| Package | Downloads |
|---|---|
|
SourceCrafter.LiteSpeedLink.ServerGenerator
Host generator for LiteSpeedLink services using Memory, UDS, QUIC, TCP and UDP |
|
|
SourceCrafter.LiteSpeedLink.ClientGenerator
Client generator for LiteSpeedLink services using Memory, UDS, QUIC, TCP and UDP |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.26.286.181 | 109 | 10/13/2026 |
| 2.26.283.228 | 134 | 10/10/2026 |
| 2.26.282.289 | 106 | 10/9/2026 |
| 2.26.282.142 | 135 | 10/9/2026 |
| 2.26.273.86 | 123 | 9/30/2026 |
| 2.26.265.50 | 115 | 9/22/2026 |
| 2.26.263.311 | 97 | 9/20/2026 |
| 2.26.262.371 | 103 | 9/19/2026 |
| 2.26.262.287 | 107 | 9/19/2026 |
| 2.26.262.202 | 101 | 9/19/2026 |
| 2.26.261.445 | 96 | 9/18/2026 |
| 2.26.249.144 | 160 | 9/6/2026 |
| 2.26.233.161 | 118 | 8/21/2026 |
| 2.26.227.290 | 126 | 8/15/2026 |
| 2.26.226.430 | 128 | 8/14/2026 |
| 2.26.226.81 | 117 | 8/14/2026 |
| 2.26.212.228 | 130 | 7/31/2026 |
| 2.26.212.105 | 124 | 7/31/2026 |
| 2.26.211.464 | 116 | 7/30/2026 |
| 2.26.206.340 | 124 | 7/25/2026 |