-
Notifications
You must be signed in to change notification settings - Fork 2
core modules service registration
NetFusion allows Plugin Modules to register service components with Microsoft's IServiceCollection. Services are added by overriding base methods provided by the PluginModule parent class. Similar to the other base PluginModule method overrides, the registration methods are called in a specific order. This topic will review the provided registration methods and when each one should be used.
The registration of service components within modules allows the host's Program class to remain void of large amounts of registrations. Also, the NetFusion plugin architecture allows for easy code reuse between microservices since all service registration details are encapsulated within a plugin modules. Having all registrations contained within a plugin, does not require the developer of the consuming microservice to understand the details of how the services should be registered.
This section will discuss the two registration methods that can be overridden. The below methods are called in the order listed on each module. These methods are first called on all Core plugin modules, followed by all Application plugin modules, and ending with the Host plugin modules. This order allows any services registered by core plugin modules to be overridden by application plugin modules. Then finally these methods are called on the single Host plugin modules allowing it to override any services registered by core and application plugins.
Method | When to Use |
---|---|
RegisterServices | This is the first registration method called and passed a reference to IServiceCollection. This is the most common overridden method. |
ScanForServices | This method allows a plugin to register service components for types matching a specified criteria instead of manual registration. A reference to ITypeCatalog is passed and used to select types to be added Microsoft's IServiceCollection. The ITypeCatalog has methods accepting a predicate having a type passed to which the delegate returns true if the type matches the specified criteria. All types matching the predicate are added the the DI Container using the specified scope. |
When the ScanForServices method is overridden to search for types to register as services, the type of plugin in which the module resides is taken into account.
Plug-in Type | Plug-in Types Searched |
---|---|
Core | The ITypeCatalog passed to the ScanForServices method is populated with types from all registered plugins. This is by design since core plugins provide reusable infrastructure implementations used by all types of plugins. |
Application | Since application plugins implement application-specific business logic and not reusable services, the ITypeCatalog passed to the ScanForServices method is only populated with types from registered application plugins and the host plugin. |
Host | There is only one host plugin and is located within the process started to execute the microservice. The ITypeCatalog passed to the ScanForServices method is only populated with types contained within the host plugin. |
This section will define a service contract and a default implementation, within the Examples.Bootstrapping.CrossCut project that will be overridden within the next section. This project is a Core plugin so its registration methods will be called first. Define the service contract and default implementation at the following location: Examples.Bootstrapping.CrossCut/
nano ./Core.Component/IValidNumbers.cs
namespace Examples.Bootstrapping.CrossCut;
public interface IValidNumbers
{
IEnumerable<int> GetValidNumbers();
}
namespace Examples.Bootstrapping.CrossCut;
public class DefaultValidNumberComponent : IValidNumbers
{
public IEnumerable<int> GetValidNumbers() => new[] {100, 200, 300};
}
Override the RegisterServices method within the CoreModuleTwo plug-in module.
Examples.Bootstrapping.CrossCut/Plugin/Modules/
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetFusion.Common.Base;
using NetFusion.Core.Bootstrap.Plugins;
namespace Examples.Bootstrapping.CrossCut.Plugin.Modules;
public class CoreModuleTwo : PluginModule
{
// ...
public override void RegisterServices(IServiceCollection services)
{
services.AddSingleton<IValidNumbers, DefaultValidNumberComponent>();
}
}
For testing, add the following controller injecting the IValidNumbers service.
Examples.Bootstrapping.WebApi/Controllers/
using Examples.Bootstrapping.CrossCut;
using Microsoft.AspNetCore.Mvc;
namespace Examples.Bootstrapping.WebApi.Controllers;
[ApiController, Route("api/registrations")]
public class RegistrationsController : ControllerBase
{
private readonly IValidNumbers _validNumbers;
public RegistrationsController(IValidNumbers validNumbers)
{
_validNumbers = validNumbers;
}
[HttpGet("valid/numbers")]
public IActionResult GetValidNumbers() => Ok(_validNumbers.GetValidNumbers());
}
Execute the microservice and make a request to the above controller method.
cd ./src/Examples.Bootstrapping.WebApi/
dotnet run
This section will define the RegisterServices method within an Application plugin Module to override the default IValidNumbers implementation shown above. Define a service component within the Examples.Bootstrapping.App project implementing the IValidNumbers interface.
using System.Collections.Generic;
using Core.Component;
namespace App.Component
{
public class ValidNumberComponent : IValidNumbers
{
public IEnumerable<int> GetValidNumbers() => new[] {888, 999, 1111};
}
}
Within the AppModuleOne class, register this service component for IValidNumbers.
Examples.Bootstrapping.App/Plugin/Modules/AppModuleOne.cs
using System;
using Examples.Bootstrapping.App.Plugin.Configs;
using Examples.Bootstrapping.CrossCut;
using Examples.Bootstrapping.CrossCut.Plugin;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetFusion.Common.Base;
using NetFusion.Core.Bootstrap.Plugins;
namespace Examples.Bootstrapping.App.Plugin.Modules;
public class AppModuleOne : PluginModule
{
// ...
public override void RegisterServices(IServiceCollection services)
{
services.AddSingleton<IValidNumbers, ValidNumberComponent>();
}
}
Validate that the IValidNumbers service component registered within the application plug-in module is now invoked:
cd ./src/Examples.Bootstrapping.WebApi/
dotnet run
If you examine the log entry for the Application Compontent within SEQ, you will see that the IValidNumbers service was registered:
This example shows scanning for types implementing a specific interface and registering them within the Service Collection. Create the following interface within the Examples.Bootstrap.CrossCut project for which concrete types can be derived:
namespace Examples.Bootstrapping.CrossCut;
public interface INumberGenerator
{
int GenerateNumber();
}
Next, create classes within the Examples.Bootstrapping.App and Examples.Bootstrapping.WebApi projects implementing this interface:
using System;
using Examples.Bootstrapping.CrossCut;
namespace Examples.Bootstrapping.App;
public class NumberGenerator : INumberGenerator
{
public int GenerateNumber()
{
Random r = new Random();
return r.Next(0, 100);
}
}
using Examples.Bootstrapping.CrossCut;
namespace Examples.Bootstrapping.WebApi;
public class NumberGenerator : INumberGenerator
{
public int GenerateNumber()
{
Random r = new Random();
return r.Next(200, 300);
}
}
Within the Examples.Bootstrapping.CrossCut project define the following simple service that will inject all INumberGenerator instances and return a list of generated numbers:
namespace Examples.Bootstrapping.CrossCut;
public class GeneratorService
{
private readonly IEnumerable<INumberGenerator> _generators;
public GeneratorService(IEnumerable<INumberGenerator> generators)
{
_generators = generators;
}
public int[] GetRandomNumbers() => _generators.Select(g => g.GenerateNumber()).ToArray();
}
Override the ScanForServices method within the following module contained within the example Core plugin module. Also register the GeneratorService in the container by overriding the RegisterServices method:
Examples.Bootstrapping.CrossCut/Plugin/Modules/CoreModuleTwo
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NetFusion.Common.Base;
using NetFusion.Common.Extensions.Reflection;
using NetFusion.Core.Bootstrap.Catalog;
using NetFusion.Core.Bootstrap.Plugins;
namespace Examples.Bootstrapping.CrossCut.Plugin.Modules;
public class CoreModuleTwo : PluginModule
{
// ...
public override void RegisterServices(IServiceCollection services)
{
// ..
services.AddSingleton<GeneratorService>();
}
public override void ScanForServices(ITypeCatalog catalog)
{
catalog.AsService<INumberGenerator>(
t => t.IsConcreteTypeDerivedFrom<INumberGenerator>(),
ServiceLifetime.Singleton);
}
}
The following will inject the GeneratorService into a WebApi controller and return the list of random numbers:
using Examples.Bootstrapping.CrossCut;
using Microsoft.AspNetCore.Mvc;
namespace Examples.Bootstrapping.WebApi.Controllers;
[Route("api/generators")]
[ApiController]
public class GeneratorController : ControllerBase
{
private readonly IEnumerable<INumberGenerator> _generators;
public GeneratorController(
IEnumerable<INumberGenerator> generators)
{
_generators = generators;
}
[HttpGet("numbers")]
public int[] GenerateNumbers()
{
var numbers =_generators.Select(g => g.GenerateNumber())
.ToArray();
return numbers;
}
}
Run the microservice and make a call to the controller action method:
cd ./src/Examples.Bootstrapping.WebApi/
dotnet run
The next topic will show how plugin modules can add details to their plugin's log.
-
Templates
-
Resources
-
Bootstrapping
-
Modules Details
-
Settings
-
Validation
-
Monitoring
- Setup
- Commands
- Queries
- Domain Events
- Message Logs
- Message Publishers
- Message Enrichers
- Message Filters
-
Azure Service Bus
-
RabbitMQ
-
Redis
-
MongoDB