Available starting version 4.2.0-beta.1
Contract-less composition handlers allow to write composition handlers using a syntax like the following:
namespace Snippets.Contractless.CompositionHandlers;
class SampleCompositionHandler
{
[HttpGet("/sample/{id}")]
public Task SampleMethod(int id, [FromQuery(Name = "c")]string aValue, [FromBody]ComplexType ct)
{
return Task.CompletedTask;
}
}
Compared to what's available today, when using classes implementing ICompositionRequestsHandler
, contract-less composition request handlers allow grouping handlers belonging to the same logical context instead of being forced to artificially split them into multiple classes because of the need to implement an interface.
The syntax is nonetheless similar to ASP.Net controller actions. At compilation time, ServiceComposer identifies contract-less composition request handlers by matching classes and methods against a set of conventions:
- Contract-less composition request handlers must be defined in a
CompositionHandlers
namespace or a namespace ending with.CompositionHandlers
. - Contract-less composition request handlers class names must be suffixed with
CompositionHandler
- The contract-less composition request handler class can contain one or methods; these methods must:
- Be either
public
orinternal
- Return a
Task
- Be decorated with one, and only one,
Http*
attribute (known limitation for now) - Can use the standard ASP.Net
From*
attributes to control model binding
- Be either
Given a class like the above one, a C# source generator will create a class like the following:
// <auto-generated/>
using CompositionHandlers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using ServiceComposer.AspNetCore;
using Snippets.Contractless.CompositionHandlers;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
#pragma warning disable SC0001
namespace Snippets.Contractless.CompositionHandlers.Generated
{
[EditorBrowsable(EditorBrowsableState.Never)]
class SampleCompositionHandler_SampleMethod_int_id_string_aValue_Snippets_Contractless_CompositionHandlers_ComplexType_ct(Snippets.Contractless.CompositionHandlers.SampleCompositionHandler userHandler)
: ICompositionRequestsHandler
{
[HttpGetAttribute("/sample/{id}")]
[BindFromRoute<Int32>("id")]
[BindFromQuery<String>("c")]
[BindFromBody<ComplexType>()]
public Task Handle(HttpRequest request)
{
var ctx = HttpRequestExtensions.GetCompositionContext(request);
var arguments = ctx.GetArguments(this);
var p0_id = ModelBindingArgumentExtensions.Argument<Int32>(arguments, "id", BindingSource.Path);
var p1_c = ModelBindingArgumentExtensions.Argument<String>(arguments, "c", BindingSource.Query);
var p2_ct = ModelBindingArgumentExtensions.Argument<ComplexType>(arguments, "ct", BindingSource.Body);
return userHandler.SampleMethod(p0_id, p1_c, p2_ct);
}
}
}
#pragma warning restore SC0001
Both classes will be registered in DI, allowing the injection of dependencies into the user contract-less composition handler. If the user contract-less composition handler defines more than one method matching the abovementioned conventions, one class for each method will be generated. The generated class name will guarantee uniqueness.