Wisej.NET
Ask or search…
K

Dependency Injection

Services and Dependency Injection in 3.1.
Starting with Wisej.NET 3.1 we added full support for services and dependency injection. Like everything else in Wisej.NET, it's a very lean, performant, and flexible implementation.
If you need something more complex, we also support switching our IServiceProvider with any third party implementation, including Microsoft's DI, Autofac or others.
Our DI implementation is supported in both .NET 4.8 and .NET 6+.

Registering Services

You can register services at any point in the application. Services are always registered globally so if a service type is registered more than once, subsequent definitions will replace the existing definition.
A good place to initialize the services in the static Program constructor. However, you can register services anywhere else in your code.
static Program() {
// Register an ILogger service implemented by MyLogger as a global shared singleton.
Application.Services.AddService<ILogger, MyLogger>();
// Register an IFileSystem service where the implementation is crated by the CreateFileSystemFactory method
// and scope is set to be the session.
Application.Services.AddService<IFileSystem>(CreateFileSystemFactory, ServiceLifetime.Session);
}
private static IFileSystem CreateFileSystemFactory(Type serviceType) {
return new MyFileSystemImplementation();
}
The code above registers 2 services.
One is an ILogger implementation. Wisej.NET will create an instance if MyLogger only once (Global scope) the first time it's used. This service will never be disposed, if it implements IDisposable, because it's registered using the Shared lifetime making it a singleton instance used by all sessions.
The second line registers a service of type IFileSystem and delegates the creation of the implementation to the CreateFileSystemFactory method, and sets the scope to Session. In this case, Wisej will invoke CreateFileSystemFactory the first time the service is requested per session. The service instance will be automatically disposed, if it implements IDisposable, at the end of the session.

Using a Service

Your code can request a service implementation in three ways:
  • Using Application.Services.GetService<T>().
  • Using the [Inject] attribute on a property.
  • Adding a service argument to the constructor of another service implementation.
Application.GetService<T>() returns the service implementation for the requested T type, if it exists, or null. You can call it at any time.
Using the [Inject] attribute works by default only on top-level UI containers: Page, Form, and Desktop classes. When you add the [Inject] attribute to a property (either protected, private or public), Wisej.NET will automatically resolve the service and assign it to the property during the construction of the object.
public class MyFileSystemImplementation : IFileSystem {
[Inject]
private ILogger Logger {get; set;}
[Inject(Required = true]
protected IVolumeManager Logger {get; set;}
}
To use the [Inject] attribute on any object, call Application.Services.Inject(object).
Services created by Wisej.NET can receive an instance of another services simply by declaring the requested service in the constructor.
public class MyLoggerImplementationService : ILogger {
public MyLoggerImplementationService(IFileSystem fileSystemService, ...) {
// Wisej.NET resolves all the services in the constructor, if possible.
// Unresolved services are simply set to null.
}
}

Service Injection

Wisej.NET supports Dependency Injection in two ways: Constructors and Property Injection.
Property injection is supported automatically for all top level containers (Form, Page, Desktop) and all services created by the system. Constructor injection is supported for service creation.
For example, if MyFileSystemImplementation was declared like this:
public class MyFileSystemImplementation : IFileSystem {
[Inject]
private ILogger Logger {get; set;}
// Notes: The property can be protected, private or public.
// If the service doesn't exist it will be null, unless the attribute is set to be required:
// [Inject(Required=true)] in this case it throws an exception if the service is not available.
}
Wisej.NET automatically assigns the Logger property with the registered ILogger service instance when the service is created. Another way is to declare a custom constructor (it works only for services created by the system):
Application.Services.AddService<IFileSystem, MyFileSystemImplementation>(ServiceLifetime.Session);
...
public class MyFileSystemImplementation : IFileSystem {
public MyFileSystemImplementation(ILogger logger, more services...) {
// This constructor receives all the services it declared, if available, or null.
// Wisej.NET detects circular dependencies and throws an exception instead of crashing
// the server with a StackOverflow.
}
}
You can also inject properties into any object programmatically:
Application.Services.Inject(object);
If the object declares any [Inject] properties they will receive the service instance according to the service lifetime.

Alternative IServiceProvider

In case you want to use something else to manage services, simply register the other IServiceProvider object.
// ASP.NET Core startup
public class Startup {
public static void Main(string[] args) {
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
WebRootPath = "./"
});
var app = builder.Build();
// Register Microsoft's IServiceProvider with Wisej.NET.
Application.Services.AddService<IServiceProvider>(app.Services);
app.UseWisej();
app.UseFileServer();
app.Run();
}
}
The line Application.AddService<IServiceProvider>(app.Services); registers Microsoft's IServiceProvider with Wisej.NET and replaces our IServiceProvider.
From this point on, calling Application.Services.AddService<T>() is not supported, all services have to be registered using Microsoft's DI.
Application.Services.GetService<T>() and [Inject] now use the alternative IServiceProvider to resolve services. The IServiceProvider instance is registered as a global singleton by default, but you can also register it per session using a different ServiceLifetime option.

Services Lifetime

We support the following service lifetimes:
  • Shared: The service instance is a singleton used by all sessions and all threads.
  • Session: The service instance is created per session and disposed (if IDisposable) when the session ends. Note that no other DI container supports the session lifetime.
  • Thread: The service instance is created per thread (corresponds roughly to requests from the browser) and disposed (if IDisposable) when the thread code ends.
  • Transient: The service instance is created every time it's requested. It's never disposed, the responsibility of disposing (if IDisposable) is with the caller.
The default lifetime, if not specified, when registering a service is always Shared.