Injector.NET 0.0.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package Injector.NET --version 0.0.1
NuGet\Install-Package Injector.NET -Version 0.0.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="Injector.NET" Version="0.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Injector.NET --version 0.0.1
#r "nuget: Injector.NET, 0.0.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.
// Install Injector.NET as a Cake Addin
#addin nuget:?package=Injector.NET&version=0.0.1

// Install Injector.NET as a Cake Tool
#tool nuget:?package=Injector.NET&version=0.0.1

Injector Build Status

A featherweight dependency injector written in C#.

Overview

Dependency Injection is a design pattern that helps you separate the dependencies of your code from its behavior. Additionaly it makes the code easy to test by let you mock the dependencies in your unit tests. This library provides several mechanisms to register, acquire and inherit services. Let's see how!

About services in general

  • They are interfaces (including the injector itself).
  • They are hosted in an injector.
  • Every service can be requested multiple times but can be registered only once.
  • Services are instantiated only when they are requested.
  • Every service has its own lifetime, which can be:
    • Singleton: Instantiated only once on the first request and released automatically when the containing injector is released.
    • Transient: Instantiated on every request and the caller is responsible for freeing the requested services.

Injector lifetime

The lifetime of an injector is the following:

  1. Instantiating the injector
  2. Registering new services
  3. Updating existing services
  4. Locking the injector
  5. Requesting services (even parallelly)
  6. Destroying the injector (either arbitrarily or automatically), see below

Instantiating the injector

Before we'd start we need to instantiate the root injector first:

using(IInjector injector = Injector.Create()){...}

Why root? Because it has no parent injector which means we are responsible for freeing (calling the Dispose() on) it. Child injectors (created by injector.CreateChild() call) can also be disposed arbitrarily, but it's not mandatory because the parent takes care of it:

using(IInjector injector = Injector.Create())
{
  IInjector child_1 = injector.CreateChild();
  Assert.That(child_1.Parent == injector);
  .
  .
  using(IInjector child_2 = injector.CreateChild())
  {
    Assert.That(child_2.Parent == injector);
    .
    .
  } // child_2 released here
} // child_1 released here

Remarks:

  • Child injectors inherit their parent's services, but modifying them does not affect their parents.
  • Every application should have only one root injector.

Registering new services

Registering a service can be done via several patterns (I name them recipes):

  • Service recipe: This is the most common way to file a service. To register a simple service just call the Service() generic method with the desired interface, implementation and lifetime:
    injector.Service<IMyService, MyService>(Lifetime.Transient);
    
    You can register generic services as well:
    injector.Service(typeof(IMyGenericService<>), typeof(MyGenericService<>), Lifetime.Singleton);
    
    Later you may request the specialized version of the service without registering it:
    injector.Get<IMyGenericService<string>>();
    
    Remarks:
    • Implementations must not have more than one public constructor!
    • A service can request other services via the constructor parameters:
      public MyService(IInjector injector, IService_1 dep1, IService_2 dep2){...}
      
    • Instatiating is done on the request (Get() call).
  • Lazy recipe: Similar to the service recipe except that the implementation is unknown in build time (e.g. the service implementation is placed in a module we want to load only if the implementation is requested). So instead of passing the implementation we provide a resolver which will resolve the implementation (NOT a service instance) on the first request:
    injector.Lazy<IMyService>(new MyServiceResolver(), Lifetime.Transient);
    
    Note that we can also register generic services via this method.
  • Factory recipe: As the name suggests services registered by this way have a factory function (that is called on the first request):
    injector.Factory<IMyService>(thisInjector => new MyService(), Lifetime.Singleton);
    
    It can be useful e.g. if the service has more than one public constructor. In addition we can also register generic services, in this case the factory function will be called with the specialized interface:
    injector.Factory(typeof(IMyGenericService<>), (thisInjector, specializedIMyGenericService) => MyActivator.CreateInstance(typeof(MyGenericService<>), specializedIMyGenericService.GetGenericArguments()));
    
  • Instance recipe: An instances is a "predefined value" that can act as a service:
    injector.Instance<IMyInstance>(myInstance, true);
    
    The second parameter instructs the injector to dispose the instance when the injector itself is disposed.

Note that you should not register the injector itself it is done by the system automatically.

Updating existing services

In practice, it's useful to separate common functionality (e.g. parameter validation) from the implementation. In this library this can be achieved by proxy pattern. In a brief example:

injector
  .Service<IMyModule, MyModule>()
  .Proxy<IMyModule>((thisInjector, myModuleInstance) => new ParameterValidatorProxy<IMyModule>(myModuleInstance).Proxy);

Where the ParameterValidatorProxy<TInterface> is an InterfaceProxy<TInterface> containing the parameter validation logic. And that's all. Proxy pattern can be applied against every non-generic service, in any number. Note that:

  • Trying to proxy a generic service or an instance (registered via Instance() call) will throw an exception.
  • Proxying an inherited service WILL NOT affect the parent.

Locking the injector

To prevent accidental modification of the injector, after configuring you can lock it (by call the prameterless Lock() method). After locking any attempt to modify the state of the injector will cause an exception. It's safe to call the following functions on a locked injector:

  • Get()
  • CreateChild()
  • Dispose()
    Note that Lock() affects only the instance on which it was called.

Requesting services

After finishing the configuration you can request services via the Get() method:

IMyService svc = injector.Get<IMyService>();

Keep in mind the followings:

  • Calling Get() is a thread safe operation so you may share the configured injector between different threads.
  • Requesting an unregistered or and open generic service will cause an exception.
  • Requesting services as a constructor parameter is more convenient than using the Get() method.
  • The caller should free the requested service if it is an IDisposable and has the Transient lifetime. To determine it's lifetime call the QueryServiceInfo() method.

API Docs

You can find the detailed API docs here.

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. 
.NET Core netcoreapp2.2 is compatible.  netcoreapp3.0 was computed.  netcoreapp3.1 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 (1)

Showing the top 1 NuGet packages that depend on Injector.NET:

Package Downloads
RPC.NET.Server

SDK designed for building lightweight RPC servers.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
10.1.0 265 12/28/2023
10.0.0 124 12/15/2023
9.0.0 150 6/1/2023
8.0.1 212 3/12/2023
8.0.0 222 2/25/2023
7.0.0 253 2/19/2023
7.0.0-preview5 112 1/22/2023
7.0.0-preview4 96 11/20/2022
7.0.0-preview3 142 7/24/2022
7.0.0-preview2 137 6/25/2022
7.0.0-preview1 124 4/23/2022
6.2.1 452 3/17/2022
6.2.0 411 3/15/2022
6.1.0 577 1/28/2022
6.0.0 891 12/12/2021
6.0.0-preview2 460 10/11/2021
6.0.0-preview1 226 10/9/2021
5.0.1 725 7/1/2021
5.0.0 354 6/20/2021
4.0.1 1,168 3/19/2021
4.0.0 377 3/13/2021
4.0.0-preview8 369 3/4/2021
4.0.0-preview7 313 3/2/2021
4.0.0-preview6 189 3/1/2021
4.0.0-preview5 336 2/20/2021
4.0.0-preview4 246 11/17/2020
4.0.0-preview3 314 10/7/2020
4.0.0-preview2 299 9/22/2020
4.0.0-preview1 283 8/28/2020
3.4.1 1,098 8/26/2020
3.4.0 562 8/18/2020
3.3.1 1,367 7/9/2020
3.3.0 482 6/25/2020
3.2.0 452 6/15/2020
3.1.0 484 6/3/2020
3.0.1 489 5/25/2020
3.0.0 492 5/25/2020
3.0.0-preview2 303 5/22/2020
3.0.0-preview1 318 5/22/2020
2.1.0 464 5/5/2020
2.0.1 443 4/28/2020
2.0.0 458 4/17/2020
2.0.0-preview4 319 4/14/2020
2.0.0-preview3 308 3/31/2020
2.0.0-preview2 346 3/27/2020
2.0.0-preview1 330 3/20/2020
1.3.2 472 3/6/2020
1.3.1 440 3/3/2020
1.3.0 451 2/27/2020
1.3.0-preview2 339 2/21/2020
1.3.0-preview1 318 2/14/2020
1.2.0 424 2/9/2020
1.2.0-preview1 328 2/7/2020
1.1.1 550 1/27/2020
1.1.0 517 1/27/2020
1.0.0 479 1/18/2020
1.0.0-preview7 348 1/15/2020
1.0.0-preview6 346 12/12/2019
1.0.0-preview5 320 11/27/2019
1.0.0-preview4 330 11/7/2019
1.0.0-preview3 330 10/20/2019
1.0.0-preview2 361 10/10/2019
1.0.0-preview1 352 10/9/2019
0.0.3 552 6/21/2019
0.0.2 495 6/15/2019
0.0.1 585 6/6/2019
0.0.1-a 427 6/6/2019

Initial version.