Dieser Artikel ist noch nicht in Ihrer Sprache verfügbar und wird auf Englisch angezeigt.
Hosuto 2.0 Preview.1 is out
Hosuto is our little framework for building modular .NET applications - each module gets its own isolated inner host, and you compose a single-host or a multi-host application from those modules. It has quietly done its job for years, so a major version bump is not something we do lightly.
But .NET has moved on quite a bit since we started, and a few things had piled up. So here is the first preview of Hosuto 2.0. It is a preview - the API surface is still allowed to move - but everything below is merged, tested and ready to play with.
The ValidateOnBuild surprise
Let's start with the one that actually hurt.
Since .NET 9, the generic host enables ValidateOnBuild and ValidateScopes by default in the Development environment. On its own that is a reasonable change. But if you register a hosted service by its implementation type and one of its dependencies lives in a module container (for us that is usually SimpleInjector, configured after the host is built), the built-in provider tries to validate a dependency it cannot see yet - and the whole thing blows up on startup with a cheerful Unable to resolve service for type '...'.
We first suspected .NET 10, then pinned it down to .NET 9 with a small reproduction. Two things came out of it:
AddHostedHandler<T>now registers through a factory, so the eager build-time validation no longer trips over container-only dependencies.- There is a new per-module
ValidateServiceProvider(validateScopes, validateOnBuild)option, in case you want to take back control explicitly:
builder.HostModule<MyModule>(options =>
options.ValidateServiceProvider(validateScopes: false, validateOnBuild: false));
If you ran into this on .NET 9 or 10, that is the fix.
Module contracts instead of magic method names
Historically, a Hosuto module configured itself through conventionally named methods - ConfigureServices, ConfigureContainer, Configure - that we discovered by reflection. It worked, but it was invisible to the compiler, unfriendly to trimming and AOT, and frankly a bit of a "guess the method name" game for newcomers.
2.0 introduces proper interfaces for all of that:
| You want to... | Implement |
|---|---|
| register services | IServiceConfiguringModule |
| configure the module container | IContainerConfiguringModule |
| add middleware | IApplicationConfiguringModule |
| map endpoints | IEndpointConfiguringModule |
The old convention still works - we did not want to break anyone's day - but if you implement the interface, you get IntelliSense, compile-time checking and a much better trimming story. Under the hood we also got rid of the MakeGenericType reflection at module registration, which is one more step towards being AOT-friendly.
Nothing to rewrite
Existing modules keep working as-is. The interfaces are opt-in and can be adopted one module at a time.
A minimal-API host, opt-in
Classic ASP.NET Core hosting (ConfigureWebHostDefaults plus a Startup-style Configure) is still perfectly fine, but the minimal-API WebApplication model has become the default way people build web apps. So a module can now be hosted on a minimal-API WebApplication inner host instead of the classic one - you just opt in:
builder.UseAspNetCoreMinimal();
The important bit for us: single-host and multi-host applications are still built the same way. You register modules with the same HostModule<T>() calls; only the host underneath changes. No separate code path, no "minimal edition" of your module.
Endpoints get the minimal-API treatment they deserve. Instead of app.UseRouting() / app.UseEndpoints(...), a module implements IEndpointConfiguringModule:
public void MapEndpoints(IServiceProvider services, IEndpointRouteBuilder endpoints)
{
endpoints.MapGet("/hello", () => "hi from the module");
endpoints.MapRazorPages();
}
And - because consistency matters - this also works by convention with a plain MapEndpoints(IEndpointRouteBuilder) method, just like the other module methods.
Static web assets that actually show up
Static web assets in modules is a topic that has changed a couple of times in .NET, and we changed with it more than once. On the minimal-API host, a module's own static assets (its wwwroot, Razor Pages, _content) are now served by the module host in both dotnet run (dev) and dotnet publish output.
Each module's inner WebApplication runs under its own application name, so its assets manifest is already root-mapped and every module host serves only its own files - no prefix juggling. There is a small net10 Razor sample in the repo (samples/dotnet/minimal) that hosts a Razor module and probes both a rendered page and a CSS file, so you can see it end to end.
Try it
Grab the preview packages from NuGet (Dbosoft.Hosuto and friends), and have a look at the updated Modules page in the wiki - it now documents the interfaces, the minimal-API host, endpoint mapping and the validation option.
It is a preview, so we would genuinely like to know how it feels - especially the minimal-API host and whether the module contracts read the way you would expect. Issues and thoughts are welcome over on GitHub.