IoCTools.Abstractions 1.9.1

dotnet add package IoCTools.Abstractions --version 1.9.1
                    
NuGet\Install-Package IoCTools.Abstractions -Version 1.9.1
                    
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="IoCTools.Abstractions" Version="1.9.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="IoCTools.Abstractions" Version="1.9.1" />
                    
Directory.Packages.props
<PackageReference Include="IoCTools.Abstractions" />
                    
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 IoCTools.Abstractions --version 1.9.1
                    
#r "nuget: IoCTools.Abstractions, 1.9.1"
                    
#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 IoCTools.Abstractions@1.9.1
                    
#: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=IoCTools.Abstractions&version=1.9.1
                    
Install as a Cake Addin
#tool nuget:?package=IoCTools.Abstractions&version=1.9.1
                    
Install as a Cake Tool

IoCTools

NuGet NuGet

A Roslyn source generator that lets each service declare its own lifetime, dependencies, and registration intent with small attributes. IoCTools emits constructors, service registrations, and analyzers at build time—no reflection, no runtime scanning.

Highlights

  • Self-describing services[Scoped], [DependsOn<T>], [RegisterAs<…>], and [ConditionalService] live on the class, so intent never leaves the type
  • Dependency sets – Implement IDependencySet to reuse dependency bundles across services
  • Inheritance-aware – Derived services inherit base class lifetime; diagnostics validate across the full chain
  • 100+ diagnostics – Build-time validation catches missing lifetimes, circular dependencies, lifetime mismatches, open-generic edge cases, and FluentValidation anti-patterns
  • Zero reflection – Everything happens at compile time; generated code is plain C# you can inspect

Authoring Posture

  • [Inject] is deprecated in 1.6.0 and emits IOC095. A Roslyn code fix plus ioc-tools migrate-inject migrate in bulk. Scheduled for removal in 2.0.
  • [DependsOn<T>] is the source-of-truth for explicit service dependencies.
  • [DependsOnConfiguration<T>] / [DependsOnOptions<T>] for config dependencies. InjectConfiguration stays supported but is not preferred.
  • Auto-deps (1.6.0+) declare ambient dependencies once per assembly: [assembly: AutoDep<T>], [assembly: AutoDepOpen(typeof(ILogger<>))], plus Microsoft.Extensions.Logging.ILogger<T> is auto-detected with zero configuration. See docs/auto-deps.md.

Installation

dotnet add package IoCTools.Abstractions
dotnet add package IoCTools.Generator

Or directly in your project file:

<ItemGroup>
  <PackageReference Include="IoCTools.Abstractions" Version="*" />
  <PackageReference Include="IoCTools.Generator" Version="*" PrivateAssets="all" />
</ItemGroup>

What's New in v1.9.0

See CHANGELOG.md for the full history.

Added in 1.9.0

  • [Cover<T>(ConcreteHandling = ConcreteHandling.ForceMock)] — opt-out for the auto-concrete promotion path. Previously, every concrete-class constructor dependency with an accessible parameterless constructor was silently materialized as a real instance (ParameterRole.ConcreteInstance) with a Configure{Dep}(Action<T>) helper, which lost depth-2/3 mock coverage when the SUT composed a concrete collaborator from port mocks. The new ConcreteHandling named argument (enum: Auto (default, preserves prior behavior), ForceMock) lets a test opt every non-special concrete dependency into the standard Mock<T> substitution. Existing fixtures are unchanged.
    • Constraint: ForceMock requires virtual (or abstract) public methods on the concrete target type. Moq can only intercept virtual instance methods on classes; concretes with sealed-by-default methods (the C# default) compile cleanly but Setup(...) silently no-ops and the real method body runs against default backing fields. See docs/testing.md § "Concrete Handling Modes" and the XML doc comments on CoverAttribute<>.ConcreteHandling for the interface-extraction recommendation.

Changed in 1.9.0

  • TestFixturePipeline syntax predicate tightened — the pipeline now syntactically pre-filters to types whose attribute lists name Cover / CoverAttribute before invoking the semantic model. IDE responsiveness improvement; no consumer-visible behavior change.
  • TestFixturePipeline namespace match is now exact — previously a Contains("IoCTools.Testing") substring check false-positived on consumer namespaces like MyCorp.IoCTools.Testing.Extensions.CoverAttribute<T>, emitting a duplicate fixture under the wrong attribute resolution. The check is now an exact comparison against IoCTools.Testing.Annotations.
  • Cover<T>.Logger named argument now parsed by enum-member symbol name rather than raw int value, so future reorderings of FixtureLoggerProfile enum members cannot silently misclassify. Falls back to the prior integer comparison defensively.

Fixed in 1.9.0

  • FixtureEmitter.CurrentOptionsProfile mutable-static state removed — Roslyn incremental generators must be pure functions of their inputs; a mutable static property breaks the generator cache and can race when multiple compilations run concurrently in the same generator-host AppDomain. The property was unread outside its own declaration; it has been deleted. No consumer-visible behavior change.

Changed in 1.8.0

  • [InjectConfiguration] / [DependsOnConfiguration<T>] optional complex-type sections now fall back to new T() when the section is absent — instead of forwarding null through the null-forgiving operator. Previously the generator emitted configuration.GetSection("X").Get<T>()!, which silently assigned null when the section was missing and NPE'd on first dereference. The generator now emits configuration.GetSection("X").Get<T>() ?? new T() for complex-type fields whose type has an accessible parameterless constructor. Required sections (Required = true, the default) still throw fast. Interfaces, abstract classes, and types without a parameterless constructor continue to emit the ! suppression. Collection bindings and primitive GetValue<T> bindings are unchanged. Behavior change — consumers who relied on _field == null to detect an absent optional section will now observe a default-constructed instance instead; this drove the minor-version bump rather than a patch.

Earlier — 1.7.x highlights

  • AnalysisScope model + DiagnosticGate — production-only diagnostics (IOC081/IOC082/IOC086) auto-exempt test projects via the IsTestProject MSBuild property forwarded from Microsoft.NET.Test.Sdk. No naming heuristics.
  • IOC110 (Warning, configurable) — deterministic multi-impl lifetime diagnostic; replaces the previous non-deterministic single-impl selection that caused IOC012 to fire in Rider but pass silently in CLI.
  • [RegisterAs<T>] + [RegisterAsAll] compose with IHostedService — concrete registered once at the declared lifetime, companion interfaces bridged via GetRequiredService<TImpl>(), and IHostedService bridged to the same instance.
  • Test fixture generator v2 (IoCTools.Testing) — FixtureMemberPlanner separates planning from emission; generated fixtures enable nullable context, emit new modifier on derived hidden members, support GetRequiredSection/binder-style configuration reads, and treat IClock as fixture-provided.
  • Multitarget CLIIoCTools.Tools.Cli ships for both net9.0 and net10.0.

Getting Started in Three Steps

  1. Annotate a partial service

    [DependsOn<ILogger<EmailService>>]
    public partial class EmailService : IEmailService
    {
        public Task SendAsync(string to, string subject, string body) => Task.CompletedTask;
    }
    

    Tip: [Scoped] is implied for partial classes implementing interfaces. Add [Singleton]/[Transient] only when you want to change that default.

    Full getting started guide

  2. Build – IoCTools emits Add<YourAssembly>RegisteredServices() into <AssemblyName>.Extensions.Generated.

  3. Call the extension during startup

    using YourAssembly.Extensions.Generated;
    
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddYourAssemblyRegisteredServices(builder.Configuration);
    

Common Open-Generic Pattern

[Scoped]
[RegisterAsAll]
public partial class Repository<T> : IRepository<T> where T : class
{
}

That common open-generic path is supported. If you ask for InstanceSharing.Shared across open-generic interface aliases, IoCTools emits the secondary IOC095 descriptor and falls back to valid direct registrations because Microsoft.Extensions.DependencyInjection does not support open-generic factory aliases. (In 1.6.0+, the primary IOC095 descriptor is the [Inject] deprecation diagnostic — both ship under the same ID; see diagnostics.md.)

Platform Support

IoCTools works with .NET Framework 4.6.1+, .NET Core 2.0+, and .NET 5+. The generator targets netstandard2.0 internally, but your service code can use any C# features your framework supports. See platform constraints for details.

Testing with IoCTools

The IoCTools.Testing package auto-generates test fixtures, eliminating mock declaration boilerplate.

using IoCTools.Testing.Annotations;

[Cover<UserService>]
public partial class UserServiceTests
{
    [Fact]
    public void Test() {
        var result = Sut.GetById(1); // Lazy auto-generated SUT
    }
}

Full testing guide

The CLI includes test scaffold and fixture-aware evidence --test-fixtures paths for downstream adoption:

dotnet ioc-tools test scaffold --project src/App.csproj --type MyApp.Services.UserService --dry-run
dotnet ioc-tools evidence --project tests/App.Tests.csproj --production-project src/App.csproj --test-fixtures

IoCTools CLI

IoCTools.Tools.Cli ships as a dotnet global/local tool (dotnet ioc-tools …). It interrogates your project with the real IoCTools generator.

Installation

dotnet pack IoCTools.Tools.Cli/IoCTools.Tools.Cli.csproj -c Release -o ./artifacts
dotnet tool install --global --add-source ./artifacts IoCTools.Tools.Cli

Commands

Command What it surfaces
fields --project <csproj> --file <class.cs> [--type ...] [--source] Lists generated [DependsOn] fields; outputs constructor source with --source
services --project <csproj> [--output <dir>] [--source] [--type ...] Summarizes registrations (lifetimes, interfaces, factories); outputs source with --source
explain --project <csproj> --type Namespace.Service Explains a single service: dependencies, config bindings, external flags
graph --project <csproj> [--type ...] [--format json\|puml\|mermaid] Emits a service graph in JSON/PlantUML/Mermaid
why --project <csproj> --type ... --dependency Fully.Qualified.Type Shows which generated field matches a dependency
doctor --project <csproj> [--fixable-only] Runs generator and prints diagnostics; --fixable-only filters to warnings/infos
evidence --project <csproj> [--type ...] [--settings ...] Emits one correlated evidence bundle across services, diagnostics, configuration, validators, profile, hints, and fingerprinted artifacts
config-audit --project <csproj> [--settings appsettings.json] Lists required config bindings and reports missing keys
suppress --project <csproj> [--codes IOC035,IOC092] [--json] Generates .editorconfig suppression recipes plus structured rule metadata
validators --project <csproj> [--filter ...] Lists discovered FluentValidation validators
validator-graph --project <csproj> [--why ValidatorName] [--json] Shows validator composition tree or structured lifetime explanation

Full CLI reference

Before & After: Replacing DI Smells

Legacy (manual, brittle)

public class LegacyBillingService : IBillingService
{
    private readonly ILogger<LegacyBillingService> _logger;
    private readonly IHttpClientFactory _httpClients;
    private readonly BillingOptions _options;
    private readonly IConfiguration _config;

    public LegacyBillingService(
        ILogger<LegacyBillingService> logger,
        IHttpClientFactory httpClients,
        IOptionsMonitor<BillingOptions> options,
        IConfiguration config)
    {
        _logger = logger;
        _httpClients = httpClients;
        _options = options.CurrentValue;
        _config = config;
    }
}

services.AddHttpClient();
services.Configure<BillingOptions>(configuration.GetSection("Billing"));
services.AddScoped<IBillingService, LegacyBillingService>();

Problems: duplicated registrations, runtime config lookups, no analyzer guardrails.

IoCTools (attributes, analyzers, generated DI)

using IoCTools.Abstractions.Annotations;

[Scoped]
[DependsOn<ILogger<BillingService>, IHttpClientFactory, IClock>]
[DependsOnConfiguration<string>("Billing:BaseUrl", Required = true)]
[DependsOnConfiguration<int>("Billing:RetryCount", DefaultValue = "3")]
public partial class BillingService : IBillingService
{
    public async Task ChargeAsync(BillingRequest request)
    {
        using var client = _httpClientFactory.CreateClient("billing");
        // _logger, _httpClientFactory, _clock, _baseUrl, _retryCount all available
    }
}

Generated code creates the constructor, binds configuration, and registers everything via builder.Services.AddYourAssemblyRegisteredServices(configuration).

Attribute Reference

Complete attribute reference: docs/attributes.md

Key attributes: [Scoped], [Singleton], [Transient], [DependsOn<T>], [DependsOnConfiguration<T>], [DependsOnOptions<T>], [RegisterAs<T>], [ConditionalService], and the 1.6.0 auto-deps surface ([assembly: AutoDep<T>], [assembly: AutoDepOpen(...)], [AutoDeps<TProfile>], [NoAutoDeps], …)

Diagnostics Reference

IoCTools provides core diagnostics through IOC110, plus testing diagnostics (TDIAG01 through TDIAG08) and FluentValidation diagnostics (IOC100-IOC102). See diagnostics.md for the full list.

Error-Severity Diagnostics

Rule Summary
IOC001 Service depends on unimplemented interface
IOC002 Implementation missing lifetime attribute
IOC003 Circular dependency detected
IOC004 [RegisterAsAll] requires lifetime
IOC011 Background service must be partial
IOC012 Singleton depends on Scoped
IOC014 Background service with non-Singleton lifetime
IOC015 Lifetime mismatch in inheritance chain
IOC016 Invalid configuration key
IOC018 [InjectConfiguration] requires partial class
IOC021 [ConditionalService] requires lifetime
IOC028 [RegisterAs] without service indicators
IOC029 [RegisterAs] specifies unimplemented interface
IOC031 [RegisterAs] specifies non-interface type
IOC041 Manual constructor conflicts with IoCTools
IOC049 Dependency set with non-metadata members
IOC050 Dependency set cycle detected
IOC051 Dependency set name collision
IOC077 Manual field shadows generated dependency
IOC080 Code-generating attributes require partial
IOC081 Manual registration duplicates IoCTools
IOC082 Manual registration lifetime differs
IOC087 Transient depends on Scoped
IOC088 Configuration circular reference
IOC092 typeof() registration lifetime mismatch
TDIAG04 [Cover<T>] service has no generated constructor
TDIAG05 Test class with [Cover<T>] must be partial
TDIAG07 Fixture setup helper called after Sut access
TDIAG08 Test class manually constructs a service that could use [Cover<T>]

View all diagnostics including warnings and info

Configuration

IoCTools reads configuration from MSBuild properties/.editorconfig. Common knobs:

Property Purpose Example
IoCToolsNoImplementationSeverity, IoCToolsManualSeverity, IoCToolsLifetimeValidationSeverity Override analyzer severity per category build_property.IoCToolsNoImplementationSeverity = error
IoCToolsDisableDiagnostics Disable all IoCTools diagnostics true
IoCToolsIgnoredTypePatterns Patterns for cross-assembly interfaces to ignore *.Abstractions.*;*.Contracts.*;*.Interfaces.*
IoCToolsDefaultServiceLifetime Sets the implicit lifetime when no explicit attribute Scoped | Singleton | Transient

See configuration reference

Samples & License

  • IoCTools.Sample demonstrates every attribute, diagnostic, and configuration scenario
  • Licensed under MIT. See LICENSE
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.
  • .NETStandard 2.0

    • 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
1.9.1 88 5/23/2026
1.8.0 1,192 5/12/2026
1.7.3 1,545 5/6/2026
1.7.2 88 5/6/2026
1.7.1 94 5/6/2026
1.6.1 98 4/29/2026
1.6.0 91 4/29/2026
1.5.1 143 4/12/2026
1.4.0 211 3/21/2026
1.3.0 118 1/24/2026
1.2.0 403 11/18/2025
1.1.0 294 11/12/2025
1.0.0 198 9/10/2025
1.0.0-alpha 243 8/28/2025
0.4.1 177 11/30/2024
0.4.0 150 11/30/2024
0.3.0 213 3/18/2024
0.2.6 186 2/6/2024
0.2.5 308 2/6/2024
0.2.4 183 2/6/2024
Loading failed

v1.9.0: version-coherence rebuild — no functional changes in this package. The 1.9.0 feature work landed in IoCTools.Testing / IoCTools.Testing.Abstractions ([Cover<T>] ConcreteHandling knob, TestFixturePipeline namespace-exact match, FixtureEmitter static-mutable purge). Carries forward the 1.8.0 [InjectConfiguration] null-fallback behavior change. See CHANGELOG.md.