Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Canister Icon Canister

.NET PublishCoverage StatusNuGet

Canister is one of the easiest ways to get IoC configuration under control. No longer do you have to search for that one class that you forgot to register. Instead use Canister to handle discovery and registration for you using a simple interface.

Table of Contents

Quick Start

usingMicrosoft.Extensions.DependencyInjection;varservices=newServiceCollection();services.AddCanisterModules();varprovider=services.BuildServiceProvider();

For a more detailed example, you can check out the Canister Example which demonstrates how to use Canister in a couple simple scenarios.

Basic Usage

The system has a fairly simple interface and only a couple of functions that need explaining. The first is setup:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules();}

AddCanisterModules will automatically scan assemblies for modules and load them accordingly. Or if you're doing a desktop app:

varServices=newServiceCollection().AddCanisterModules();

Note that if you like, you can control which assemblies are searched:

publicvoidConfigureServices(IServiceCollectionservices){
...services.AddCanisterModules(configure =>configure.AddAssembly(typeof(Startup).Assembly));}

Note: For security reasons, it's recommended to explicitly specify which assemblies to scan. By default, Canister will search all assemblies found in the entry assembly's top-level directory.

It is also possible to add logging to the system while configuring it. This is useful for debugging purposes or to get insights into the registration process. You can do this by passing an ILogger instance to the UseLogger method along with a default log level:

publicvoidConfigureServices(IServiceCollectionservices,ILoggerlogger){
...services.AddCanisterModules().UseLogger(logger,LogLevel.Information);}

Modules

Canister uses the concept of modules to wire things up, but is not a requirement. This allows you to place registration code in libraries that your system is using instead of worrying about it in every application. Simply add your library and Canister will automatically wire it up for you. In order to do this, under Canister.Interfaces there is the IModule interface. This interface, when implemented, has two items in it. The first is a property called Order. This determines the order that the modules are loaded in. The second is a function called Load:

publicclassTestModule:IModule{publicintOrder=>1;publicvoidLoad(IServiceCollectionbootstrapper){bootstrapper.AddAllTransient<IMyInterface>();bootstrapper.AddTransient<MyType>();}}

The module above is loaded automatically by the system and will have the Load function called at initialization time. At this point you should be able to resolve and register classes using the bootstrapper parameter. The service collection also has a couple of extra extension methods: AddAllTransient, AddAllScoped, AddAllSingleton:

bootstrapper.AddAllTransient<IMyInterface>();

The AddAllxxxx functions will find everything that implements a class or interface in the Assemblies that you tell it to look in and will register them with the service collection.

Attributes

Canister also allows for attributes to be used to control registration. There are two attributes that the system uses:

  • RegisterAttribute - This attribute is used to control how a class is registered. It will register the class as all interfaces that it implements as well as the class itself. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient. It also can take a service key as well.
[Register(LifeTime.Singleton)]publicclassMyType:IMyInterface{}
  • RegisterAllAttribute - This attribute is used to control how an interface is registered. It will register all classes that implement the interface similar to the AddAllxxxx functions. The attribute takes the life time of the registration as a parameter. If no parameter is given, the registration will be transient.
[RegisterAll(LifeTime.Singleton)]publicinterfaceIMyInterface{}

Canister Extension Methods

Canister provides a set of extension methods to streamline your IoC (Inversion of Control) container registration code. These methods offer convenient ways to conditionally register services based on certain criteria, enhancing the flexibility of your application's dependency injection setup. Note that these can be used even if you are not using the Canister modules.

1. AddTransientIf()

The AddTransientIf method registers a service as transient only if a specified condition is met. This is useful when you want to dynamically determine whether a service should be transient or not.

services.AddTransientIf<IMyService,MyService>(services =>condition);

2. AddScopedIf()

Similar to AddTransientIf, AddScopedIf registers a service as scoped based on a given condition.

services.AddScopedIf<IMyScopedService,MyScopedService>(services =>condition);

3. AddSingletonIf()

The AddSingletonIf method registers a service as a singleton if the specified condition holds true.

services.AddSingletonIf<IMySingletonService,MySingletonService>(services =>condition);

4. AddKeyedTransientIf(), AddKeyedScopedIf(), AddKeyedSingletonIf()

These methods follow the same pattern as their non-keyed counterparts but additionally allow you to register services with a specified key.

services.AddKeyedTransientIf<IService>(key,implementationType,(services,key)=>condition);

5. Exists()

The Exists method checks whether a service with a specific type and, optionally, a key, has already been registered. This can be helpful in avoiding duplicate registrations or finding issues with your environment before starting the application.

if(!services.Exists<IMyService>()){services.AddTransient<IMyService,MyService>();}

6. AddAllTransient(), AddAllScoped(), AddAllSingleton()

These methods allow you to register all implementations of a given interface or class as transient, scoped, or singleton services, respectively. They are particularly useful for bulk registrations.

services.AddAllTransient<IMyService>();services.AddAllScoped<IMyScopedService>();services.AddAllSingleton<IMySingletonService>();

7. TryAddAllTransient(), TryAddAllScoped(), TryAddAllSingleton()

These methods attempt to register all implementations of a given interface or class as transient, scoped, or singleton services, but only if they have not already been registered. This is useful for ensuring that you do not accidentally override existing registrations.

services.TryAddAllTransient<IMyService>();services.TryAddAllScoped<IMyScopedService>();services.TryAddAllSingleton<IMySingletonService>();

8. Decorate()

The Decorate method allows you to wrap an existing service with a decorator. This is useful for adding additional behavior to a service without modifying its original implementation.

services.Decorate<IMyService,MyServiceDecorator>();

9. AddCanisterModules()

The AddCanisterModules method is used to automatically discover and register modules that implement the IModule interface. This method scans the specified assemblies for modules and loads them, allowing you to organize your service registrations in a modular way.

services.AddCanisterModules(config =>{// Optionally specify which assemblies to scanconfig.AddAssembly(typeof(MyModule).Assembly).UseLogger(logger,LogLevel.Information);});

10. GetRegistrationsSummary()

The GetRegistrationsSummary method provides a summary of all registered services in the IoC container. This can be useful for debugging and understanding what services are available in your application.

varsummary=services.GetRegistrationsSummary();logger.LogInformation("Service Registrations: {Summary}",summary);

Usage Example

Here's an example of how you might use these methods:

IHostEnvironment?environment;// Conditionally register a transient service if in development environment.services.AddTransientIf<IMyService,MyDebugService>(_ =>environment.IsDevelopment());// However if you're in production, add a different implementation.services.AddTransientIf<IMyService,MyProductionService>(_ =>environment.IsProduction());// Check if a keyed service is missing and log a warning if so.if(!services.Exists<IService>(key)){logger.LogWarning("Service {Service} is missing",key);}

These methods empower you to create more dynamic and adaptive dependency injection configurations tailored to your application's requirements.

Working With Other IoC Containers

While the library assumes you are using the built in ServiceCollection, it is possible to work with IoC containers. All that is required is that it implements the IServiceCollection interface.

Using Canister in Your library

If you wish to use Canister in your library, it is recommended that you build an extension method off of the ICanisterConfiguration interface that will allow you to register your needed assemblies for the user to make the experience a bit simpler when they want to control configuration themselves.

Installation

The library is available via Nuget with the package name "Canister.IoC". To install it run the following command in the Package Manager Console:

dotnet add package Canister.IoC

Build Process

In order to build the library you may require the following:

  1. Visual Studio 2026 or VS Code with the C# extension.
  2. .NET 10 SDK or later.

Other than that, just clone the project and you should be able to load the solution and build without too much effort.

License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

Contributing

If you would like to contribute to the project, please fork the repository and submit a pull request. Contributions are welcome, and we appreciate any help in improving the library. Please refer to the Contributing Guide for more details on how to contribute.

About

Canister is a simple C# library aimed at enhancing the built-in IoC container in .NET. It enables you to effortlessly add all objects of a specified type and introduces the concept of modules to automatically wire up your system. With Canister, managing dependencies becomes a breeze, allowing you to focus on writing maintainable code.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

8 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages