Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
S

stronginject

> 编程语言
Open source

compile time dependency injection for .NET

868 stars0 likes1 views
WebsiteGitHub

About

compile time dependency injection for .NET

Compile Time Dependency Injection For C#

Logo kindly contributed by @onelioubov and @khalidabuhakmeh

Table Of Contents

  • Aims
  • Requirements
  • NuGet
  • How It Works
  • Usage
    • Sample Projects
      • ASP.NET Core/Microsoft.Extensions.DependencyInjection
      • Console Application
      • Xamarin Application
      • WPF Application
      • Real World Projects Using StrongInject
    • Declaring a container
    • Using a container
    • Registration
      • Basics
      • Scope
      • Modules
      • Instance fields and properties
      • Factories
      • Generic Factory Methods
        • Factory Of Methods
      • Decorators
      • Providing registrations at runtime or integrating with other IOC containers
      • How StrongInject picks which registration to use
    • Delegate Support
    • Owned support
    • Post Constructor Initialization
    • Async Support
      • Parallel Resolution
    • Resolving all instances of a type
    • Optional Parameters
    • Disposal
    • Thread Safety
    • Inbuilt Modules
  • Product Roadmap
  • Contributing
  • Need Help?

Aims

  1. Compile time checked dependency injection. If the type you're resolving isn't registered you get an error at compile time, not runtime.
  2. Fast. There's no dictionary lookups, no runtime code generation. Just the fastest code it's possible to generate to resolve your type.
  3. Encourage best practices. You can't use the container as a service locator. You can't forget to dispose the resolved types.
  4. No reflection or runtime code generation. Instead StrongInject uses roslyn Source Generators, meaning it's fast, and works well on UWP/IOS too. This also means it's linker friendly - see https://devblogs.microsoft.com/dotnet/app-trimming-in-net-5/.
  5. Async support. StrongInject fully supports async initialization and disposal, a feature sorely lacking in many IOC containers.

Requirements

Visual Studio 16.8 or greater

.NET 5.0.102 SDK or greater

NuGet

https://www.nuget.org/packages/StrongInject/

``

How It Works

To use StrongInject, you first need to tell StrongInject the top-level services you would like to resolve. You do this by adding a new class implementing IContainer. This will be your container. If you want to resolve multiple top-level services, then you can implement IContainer multiple times on one container, or create multiple containers. StrongInject will then check at compile time that you've registered everything you need with the container to enable it to resolve all the top-level services. If you haven't, the compilation will fail with an error explaining what's gone wrong.

For example, if you want to resolve MyApp you might try doing this:

csharp
using StrongInject;

public class MyService {}
public class MyApp { public MyApp(MyService myService) {} }

[Register(typeof(MyApp))]
public partial class MyContainer : IContainer {}

When you try compiling, it will fail with the following error: SI0102: Error while resolving dependencies for 'MyApp': We have no source for instance of type 'MyService'.

Now you fix it by adding a registration for MyService:

csharp
using StrongInject;

public class MyService {}
public class MyApp { public MyApp(MyService myService) {} }

[Register(typeof(MyApp))]
[Register(typeof(MyService))]
public partial class MyContainer : IContainer {}

And this time when you compile, it will succeed and generate all the code needed to resolve an instance of MyApp at compile time.

What do I mean by a top-level service?

When using an IOC container, sometimes you request an instance from the container directly. These are top-level services. Most of the time the container resolves something though, you never ask for it explicitly - instead it's needed as a dependency for something else, which may itself be a dependency or a top-level service etc.

Ideally you want IOC containers to be non invasive - this means you write all your code as if there was no container, and then just use the container once to bootstrap your code. When writing code like this there should only ever be one top-level service. Sometimes this is not possible - for example when integrating with ASP.NET Core your controllers will usually need to be top-level services, but you should always try to minimize the number of top-level services where possible.

The next section will go into more detail about exactly how to register stuff with containers, and how to use them.

Usage

The wiki is currently a work in progress. It aims to give a more thorough formal overview of everything in StrongInject, whereas this section of the readme gives a briefer overview relying heavily on examples. I would read through this first, then check out the wiki if you have any questions.

Sample Projects

Check out these sample projects to help you get started:

ASP.NET Core/Microsoft.Extensions.DependencyInjection
Console Application
Xamarin Application
WPF Application

Real World Projects Using StrongInject

  • FluentLang

Declaring a container

To create a container for a specific type, declare your a partial class and implement StrongInject.IContainer:

csharp
using StrongInject;

public class A {}

[Register(typeof(A))]
public partial class Container : IContainer {}

If it's possible to resolve the type parameter StrongInject will generate the implementation of IContainer for you. Else it will produce an error diagnostic.

You can implement IContainer for different values of T. They will all share SingleInstance dependencies.

csharp
using StrongInject;

public class A {}
public class B {}

[Register(typeof(A))]
[Register(typeof(B))]
public partial class Container : IContainer, IContainer {}

Using a container

There are two ways to use a container - using the Run methods or the Resolve methods.

Either way you'll find it easier if you use the extension methods defined in StrongInject.ContainerExtensions rather than those defined directly on the container, so make sure you're using StrongInject;

The Run method on IContainer takes a Func. It resolves an instance of T, calls the func, disposes of any dependencies which require disposal, and then returns the result of the func. This ensures that you can't forget to dispose any dependencies, but you must make sure not to leak those objects out of the delegate. There are also overloads that allow you to pass in a void returning lambda.

csharp
public class Program
{
  public static void Main()
  {
    System.Console.WriteLine(new Container().Run(x => x.ToString()));
  }
}

In some cases this isn't flexible enough, for example if you want to use StrongInject from another IOC container, or you need more fine grained control over the lifetime of T.

For these cases you can call the Resolve method. This returns an Owned which is essentially a disposable wrapper over T. Make sure you call Owned.Dispose once you're done using Owned.Value.

csharp
public class Program
{
  public static void Main()
  {
    using var ownedOfA = new Container().Resolve();
    var a = ownedOfA.Value;
    System.Console.WriteLine(a.ToString());
  }
}

Registration

Basics

As you saw above, you can register a type with a container using the RegisterAttribute:

csharp
using StrongInject;

public class A {}
public class B {}

[Register(typeof(A))]
[Register(typeof(B))]
public partial class Container : IContainer, IContainer {}

All the dependencies of the container type parameter must be registered or you will get a compile time error.

By default [Register(typeof(A))] will register a type A as itself. You can however register a type as any base type or implemented interface:

csharp
using StrongInject;

public class BaseBase {}
public interface IBase {}
public class Base : BaseBase, IBase {}
public interface IA {}
public class A : Base, IA {}

[Register(typeof(A), typeof(IA), typeof(IBase), typeof(BaseBase))]
public partial class Container : IContainer {}

If you do so, you will have to explicitly also register it as itself if that is desired: [Register(typeof(A), typeof(A), typeof(IA), typeof(IBase), typeof(BaseBase))]

If you're on C# preview language version a type, you can use the generic versions of the attribute: [Register] and [Register].

If there is a single public non-parameterless constructor, StrongInject will use that to construct the type. If there is no public non-parameterless constructor StrongInject will use the parameterless constructor if it exists and is public. Else it will report an error.

You can register generic types as well:

csharp
public class A {}
public interface IB {}
public class B : IB {}

[Register(typeof(A<>))]
[Register(typeof(B), typeof(IB))]
public partial class Container : IContainer>, IContainer> {}

Scope

The scope of a registration determines how often a new instance is created, how long it lives, and who uses it.

It can be set as the second parameter of a registration:

csharp
using StrongInject;

public class A {}
public interface IB {}
public class B : IB {}

[Register(typeof(A), Scope.SingleInstance)]
[Register(typeof(B), Scope.InstancePerResolution, typeof(IB))]
public partial class Container : IContainer, IContainer {}

There are currently 3 different scopes:

Instance Per Resolution

This is the default scope.

A single instance is shared between all dependencies created for a single resolution. For example if A depends on B and C, and B and C both depend on an instance of D, then when A is resolved B and C will share the same instance of D.

Note every SingleInstance dependency defines a separate resolution, so if B and/or C are SingleInstance they would not share an instance of D.

Similarly every lambda defines a separate resolution, so if A depends on Func, then each time Func is invoked a fresh instance of both B and D will be created.

Instance Per Dependency

A new instance is created for every usage. For example even if type B appears twice in the constructor of A, two different instances will be passed into the constructor.

SingleInstance

A single instance will be shared across all dependencies, from any resolution

Modules

You can add registrations to any type, and then import them using the RegisterModuleAttribute, or by inheriting from the module. This allows you to create reusable modules of common registrations.

csharp

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C#csharpcsharp-sourcegeneratordidotnet

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言