Migrating to 2.0
2.0 upgrades the AsyncAPI object model to ByteBard.AsyncAPI.NET 4.0 and uses the major version to clean up API that came over from Microsoft.AspNetCore.OpenApi but never applied to AsyncAPI. Most apps only need the namespace change and the operation attribute renames. The changelog lists every change.
Checklist
- Update every
Bielu.*package to 2.0. They are versioned together, so mixing 1.x and 2.0 packages isn't supported. - Replace
using Bielu.AspNetCore.AsyncApi.Attributes.Attributes;withusing Bielu.AspNetCore.AsyncApi.Attributes;. - Replace
[PublishOperation]with[ReceiveOperation]and[SubscribeOperation]with[SendOperation]. Note the inversion, explained below. - If anything depends on generated operation IDs, set
OperationIdexplicitly or update the references. - Fix the compile errors from the removed and changed members.
- If something other than
AddOpenApi()describes your minimal APIs, such as Swashbuckle, callAddEndpointsApiExplorer()yourself.
Attributes namespace
The attribute types moved from Bielu.AspNetCore.AsyncApi.Attributes.Attributes to Bielu.AspNetCore.AsyncApi.Attributes, dropping the doubled segment. The types and their members are unchanged, so this is a find-and-replace:
-using Bielu.AspNetCore.AsyncApi.Attributes.Attributes;
+using Bielu.AspNetCore.AsyncApi.Attributes;
Publish/Subscribe become Receive/Send
Operations are now named from the application's point of view, as in AsyncAPI 3: [SendOperation] produces action: send and [ReceiveOperation] produces action: receive. OperationType is now Send/Receive instead of Publish/Subscribe.
The 2.x names described the operation from the client's side, so the mapping is inverted:
| 1.x | 2.0 | Meaning |
|---|---|---|
[PublishOperation] |
[ReceiveOperation] |
The app receives messages that clients publish |
[SubscribeOperation] |
[SendOperation] |
The app sends messages that clients subscribe to |
OperationType.Publish |
OperationType.Receive |
|
OperationType.Subscribe |
OperationType.Send |
Warning
A plain find-and-replace of Publish → Send reverses every operation. Map them as in the table above.
[AsyncApi]
public class LightMeasuredHandler
{
[Channel("light.measured")]
- [PublishOperation(typeof(LightMeasuredEvent))]
+ [ReceiveOperation(typeof(LightMeasuredEvent))]
public void Handle(LightMeasuredEvent message) { }
}
[PublishOperation] and [SubscribeOperation] still compile, with an [Obsolete] warning, and produce the same operations as in 1.x. You can migrate one handler at a time.
Default operation IDs change
An operation without an explicit OperationId gets the ID {Type}_{Method}_{OperationType}. The suffix follows the new names, so _Publish becomes _Receive and _Subscribe becomes _Send. Anything that refers to an operation by ID breaks, such as an Arazzo workflow's operationId, a generated client, or a test snapshot. Either update those references, or pin the old ID:
[ReceiveOperation(typeof(LightMeasuredEvent), OperationId = "LightMeasuredHandler_Handle_Publish")]
Custom operation attributes and the source generator
The source generator now emits attributes derived from [SendOperation] or [ReceiveOperation]. Before, it dropped them silently. Such an attribute must be internal or public. If it isn't, for example a private nested class, the new BASYNC011 warning reports that its operation is left out of the generated document. See Native AOT.
Removed and changed members
| 1.x | 2.0 |
|---|---|
AsyncApiOptions.ShouldInclude |
Removed. Generation never called it. Use AddIncludedChannel(...) / IncludeOnlyChannels, or a document transformer |
AsyncApiOperationTransformerContext.Description |
Removed. It was always null |
AsyncApiJsonSchemaTransformerContext.ParameterDescription |
Removed |
AsyncApiDocumentTransformerContext.DescriptionGroups |
Removed. Resolve IApiDescriptionGroupCollectionProvider from context.ApplicationServices if you need it |
GetOrCreateSchemaAsync(type, parameterDescription, cancellationToken) |
GetOrCreateSchemaAsync(type, cancellationToken) on all three contexts |
AsyncApiGeneratorConstants.DefaultAsyncApiVersion |
AsyncApiGeneratorConstants.DefaultInfoVersion. Same value ("1.0.0", the default info.version) |
A call to GetOrCreateSchemaAsync that passes cancellationToken: by name keeps compiling.
Collections are typed as interfaces
| Member | 1.x | 2.0 |
|---|---|---|
AsyncApiOptions.OperationBindings, ChannelBindings, MessageBindings, ServerBindings |
Dictionary<string, List<…>> |
IDictionary<string, IList<…>> |
AsyncApiOptions.IncludeOnlyChannels |
HashSet<string> |
ISet<string> (still case-insensitive) |
AsyncApiTypeMetadata.Members, AsyncApiMemberMetadata.Parameters, Messages, Operations, MessageExamples |
List<…> |
IReadOnlyList<…> |
Code that reads these collections or adds through the Add… methods is unaffected. Code that names the concrete type, such as Dictionary<…> bindings = options.ChannelBindings;, needs var or the interface type. A custom IAsyncApiMetadataProvider can keep passing List<…> instances, but it must build the metadata lists up front instead of adding to them after construction.
The endpoint ApiExplorer isn't registered anymore
AddAsyncApi() and AddAsyncApiForApiVersions() no longer call AddEndpointsApiExplorer(), since document generation doesn't use it. AddOpenApi() registers what it needs, and so does Asp.Versioning's AddApiExplorer(), which AddAsyncApiForApiVersions() requires, so most apps notice nothing. If something else relied on it, such as Swashbuckle's AddSwaggerGen() describing minimal APIs, register it yourself. This applies to apps that also use controllers too: AddControllers() registers the ApiExplorer for controllers only, so without the call their minimal APIs drop out of the Swagger document.
builder.Services.AddEndpointsApiExplorer();
API versioning
AddAsyncApiForApiVersions() serves one document per API version, with that version's info and deprecation flag. The documents were never filtered by version: in 1.x the ShouldInclude filter it set was never called. This is unchanged in 2.0, so each document lists every channel. To limit a version's document, include its channels in the configure callback:
builder.Services.AddAsyncApiForApiVersions((options, version) =>
{
if (version.ApiVersion.MajorVersion == 2)
{
options.AddIncludedChannel("orders.v2");
}
});
ByteBard.AsyncAPI.NET 4.0
The AsyncAPI object model (AsyncApiDocument, AsyncApiChannel, the binding types, …) comes from ByteBard.AsyncAPI.NET, which moves from 3.0 to 4.0. Its types appear in transformers, options and bindings, so code that uses them directly may need updating. See the ByteBard.AsyncAPI.NET repository for its changes. Code that only uses this library's attributes and AsyncApiOptions methods doesn't need any changes.
ByteBard 4.0 also fixes three binding bugs in 3.0, so generated documents change:
- WebSockets channel bindings use the
wskey. - Kafka message bindings write
schemaIdLocationinstead ofSchemaIdLocation. - SQS queue bindings write
fifoQueueeven when it isfalse, since the schema requires it.
In AsyncAPI 2.x documents, channel servers are now listed by name as the spec requires, and message traits without headers no longer get an empty headers schema.