-
Notifications
You must be signed in to change notification settings - Fork 0
Modules
With Hosuto modules you can create isolated parts of your application that will all run in their own host but share global settings and a DI container. As each module has it's own service provider you can register different services in each module. For example if you host a server component in one module you could configure another module to host a client of the same component. If you now distribute the application you can choose to run each module in it's own application or to run the modules in the same application.
To use modules first create a modules host in your applications main method. This host replaces the default .Net Generic Host.
Add a reference to the nuget package Dbosoft.Hosuto.Hosting and build the modules host from a ModulesHostBuilder:
static Task Main(string[] args)
{
var builder = ModulesHost.CreateDefaultBuilder(args);
// you can configure a modules host builder like a host builder.
// All configurations set with ConfigureHostConfiguration will be shared between all modules.
builder.UseEnvironment(EnvironmentName.Development);
return builder.RunConsoleAsync();
}
Then you can define your modules. A module is typical placed in its own assembly but this is not required. Modules have a convention based setup logic like the Startup class for Asp.Net Core.
public class SimpleModule
{
public void ConfigureServices(
IServiceCollection services)
{
[...]
The configure service method will configure the DI container of the module host. To do something useful you will typical have to register at least one HostedServices here.
The module has now to be added to the modules host builder:
static Task Main(string[] args)
{
var builder = ModulesHost.CreateDefaultBuilder(args);
[...]
builder.HostModule<SomeModule>();
return builder.RunConsoleAsync();
}
Hosuto supports also hosting of Asp.Net Core WebApplications in modules.
To enable the aspnetcore support you first have to add the package Dbosoft.Hosuto.Hosting.AspNetCore.
Then call method UseAspNetCore
on ModulesHostBuilder
:
For Asp.NetCore 3.0 or higher:
builder.HostModule<SampleWebModule>();
builder.UseAspNetCoreWithDefaults((module, webBuilder) =>
{
});
or without defaults
builder.HostModule<SampleWebModule>();
builder.UseAspNetCore((module, webBuilder) =>
{
});
For Asp.Net Core 2.0 and 2.1:
builder.HostModule<SampleWebModule>();
builder.UseAspNetCore(() => WebHost.CreateDefaultBuilder(args), (module, webBuilder) =>
{
});
For AspNetCore the Module has to be declared with interface IWebModule
or as WebModule
. As the module conventions are compatible with the AspNet.Core startup class conventions you use the Startup class as module and inherits it from WebModule
or IWebModule
:
public class SampleWebModule : WebModule
{
// this is optional, see sample of UseAspNetCore with HttpSys below for usage
public override string Path => "/sample";
public SampleWebModule(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
[...]
Please note that if you host multiple web modules in one application each will use their own Http server.
Therefore you have to configure each module to its own port (Kestrel) / path (HttpSys).
For Kestrel you should assign a unique port to each module.
On Windows you can also use http.sys. This has the advantage that http.sys supports port sharing and therefore you could use the path of the module instead of a port:
.UseAspNetCore((module, webHostBuilder) =>
{
webHostBuilder.UseHttpSys(options =>
{
options.UrlPrefixes.Add($"https://localhost:8080{module.Path}");
})
.UseUrls($"https://localhost:8080{module.Path}");
})
For a ApiApp based Web Module you can use any project template.
If you would like to use Blazor, Razor or assets in you WebModule you should create it first as normal Web Application from the templates.
Afterwards change project SDK from Microsoft.NET.Sdk.Web
to Microsoft.NET.Sdk.Razor
and add also the StaticWebAssetBasePath
property as in this sample:
<Project Sdk="Microsoft.NET.Sdk.Razor">
<PropertyGroup>
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
<StaticWebAssetBasePath>.modules/$(AssemblyName)</StaticWebAssetBasePath>
</PropertyGroup>
This will enable automatic handling of assets for the module, so that assets are available in Development and after packageing.
Deprectated for AspNetCore >= 3.x!!
You can use the interface INamedModule
to assign a name for the module.
This has been added for compatibilty with ASPNetCore 2.x where it will be used to set the content root.
In that case you have to use the same name for module and the project folder of the module.
For example the SampleWebModule from the code above has both a project folder name 'SampleWebModule' and Name="SampleWebModule"
(see also https://github.com/dbosoft/Hosuto/tree/master/samples/dotnetcore21).
Also project files of modules and all applications using these modules have to be in the same project directory.
After publishing also the application files have to be placed by following this convention:
- MySuperApp
- MySuperAppRoot <-- content root, typical binary folder
- ModuleA <-- content of module A
- ModuleB <-- content of module B
You can configure modules from the builder as in a normal single host application.
All calls of ConfigureServices
and ConfigureAppConfiguration
will be applied to all modules hosted in the ModulesHost.
This includes also the extension methods for logging and so on.
// ...
.ConfigureAppConfiguration(config => config
// ...
).ConfigureLogging(l=>
{
l.SetMinimumLevel(LogLevel.Trace);
});
Individual module configuration from builder is also possible:
// ...
.HostModule<MyModule>(options =>
{
options.Configure(hostBuilder =>
{
// ...
}
);
})
The .NET generic host and AspNetCore host have a build in DI container (available as IServiceProvider). It will be build by the ConfigureServices method and can be used in Configure method (AspNetCore) or for hosted services.
However, when it comes to complex applications having just one DI container may cause incompatibilties between different setups. For example enabling EfCore with more than one context requires a different setup as with one context.
Therefore - in Hosuto - each module will have it's own DI Container. It will by build up by Hosuto automatically from a internal HostBuilder with all the configuration you have applied to ModulesHost.
To inject services from the application (where you are building the ModulesHosts) you can optionally set up a additional DI container.
This container will be available for all modules.
Hosuto has build-in support for using ServiceCollection as shared DI container. You can enable it with the UseServiceCollection method:
var services = new ServiceCollection()
.Add...
// create ModulesHosts with services, service provider will be
// created automatically
return ModulesHost.CreateDefaultBuilder(args)
.UseServiceCollection(services)
The service provider can be used within modules by following convention:
If the first argument of ConfigureServices
or Configure
is a IServiceProvider
than it will be the shared container!
public class SimpleModule
{
public void ConfigureServices(
IServiceProvider sharedServiceProvder,
IServiceCollection services)
{
[...]
In method Configure
both containers are available:
public void Configure(IServiceProvider sharedServiceProvider, IApplicationBuilder app)
{
// module serviceProvider
app.ApplicationServices
}
Hosuto has a optional package to enable support for SimpleInjector: Dbosoft.Hosuto.SimpleInjector
With this package you can setup the shared container like this:
var container = new Container();
ModulesHost.CreateDefaultBuilder(args)
.UseSimpleInjector(container)
You can now access the container as IServiceProvider with the same convention as explained above.
The default behaviour of UseSimpleInjector is to create also a additional Container for each module.
This container will be automatically integrated with the ServiceCollection based container.
When using the module container following additional (optional) methods will be called by convention:
// configures the module SimpleInjector container. The IServiceProvider argument is optional
public virtual void ConfigureContainer(IServiceProvider sharedServiceProvider, Container container)
{
}
// configures how the SimpleInjector container is added to the Service Collection container
public void AddSimpleInjector(SimpleInjectorAddOptions options)
{
// example of usage for AspNetCore
options.AddAspNetCore()
.AddControllerActivation();
}
See also: AddSimpleInjector usage example for cross-wiring AspNetCore
The module container can be disabled by called UseSimpleInjector with false on second argument: UseSimpleinjector(container,false)