Go to content

Bulletcode.NET

Dependency Injection

ASP.NET Core relies on the dependency injection mechanism to ensure that applications can customize the framework by implementing custom services. Bulletcode.NET takes advantage of this by re-implementing some of the core ASP.NET services, but it also allows applications to customize its own services. To make this possible, both ASP.NET and Bulletcode.NET use the TryAdd() family of methods to register their services whenever possible.

Bulletcode.NET also relies on dependency injection in desktop applications. View models are created using the service provider, which means that dependencies can be injected into them, and their lifetime is scoped to the lifetime of the corresponding page or dialog in the UI. More information can be found in the MVVM chapter.

Built-in services

The following extension methods for registering Bulletcode.NET services are available in web applications:

  • AddConfiguration() — see Configuration
  • AddCoreMvcServices() and AddFrontViewServices() — see MVC
  • AddUserCookie(), AddUserToken() and AddApiKey() — see Authentication
  • AddRoleBasedAuthorization() — see Authorization
  • AddUserService() and AddEventService() — see Database
  • AddSqlSession() — see Session
  • AddMailService() — see Mail
  • AddStorageService() — see Storage

The following extension methods are available in desktop applications:

Service attributes

To make it easier to register custom services, you can use the following attributes: Singleton, Transient and Scoped. By default, the service type is the same as the implementation type decorated by one of these attributes:

[Singleton]
public class MyService
{
}

It is also possible to specify a custom service type used to access the service, for example:

[Singleton( typeof( IMyService ) )]
public class MyService : IMyService
{
}

If the AllowMultiple property of the service attribute is set to true, the service is registered by calling TryAddEnumerable instead of TryAdd, which means that multiple implementations of the same service can exist.

In order to register all services decorated with the service attributes, call the RegisterServices() extension method, passing the assembly containing the types to register, for example:

services.RegisterServices( typeof( Program ).Assembly );

Service aliases

The AddAlias<TService, TImplementation>() extension method makes it possible to register a class which implements two or more different services:

public class MyService : IMyService, IMyDataProvider
{
}

Normally, when you register such class as a singleton, two separate instances of the MyService class are created for each interface. To solve this problem, you can register the second service as an alias, which means that both services will share the same instance of the MyService class:

services.AddSingleton<IMyService, MyService>();
services.AddAlias<IMyDataProvider, MyService>();

AddAlias() can also be used for scoped services; in this case, both services will share the same instance in a given score. Aliases cannot be used for transient services.

Aliases can be added for services registered using the Singleton or Scoped attributes, by adding one or more Alias attributes:

[Singleton( typeof( IMyService ) )]
[Alias( typeof( IMyDataProvider ) )]
public class MyService : IMyService, IMyDataProvider
{
}

IScopedServiceFactory

ASP.NET Core supports scoped services whose lifetime is bound to a particular web requests. A similar mechanism is also implemented by Bulletcode.NET to bind the lifetime of view models and their dependencies to a particular page or dialog in a desktop application. Internally, this mechanism uses the IScopedServiceFactory, which contains a method for creating an instance of the specified type and its dependencies within its own scope, and to destroy the service together with its scope.

In order to use the IScopedServiceFactory to create custom scopes, call the AddScopedServiceFactory() extension method.