Skip to content

Conversation

@danielmarbach
Copy link
Contributor

@danielmarbach danielmarbach commented Feb 5, 2026

This PR introduces a new Roslyn analyzer (NSBENV001) that warns developers when they use NServiceBus APIs that are not supported in specific runtime environments (e.g., Azure Functions isolated worker).

Changes

Core Runtime (NServiceBus.Core)

  • EnvironmentIds.cs: Internal constants class containing environment identifiers
    • Serverless: Identifier for Serverless environments
  • NotSupportedInEnvironmentAttribute.cs: New attribute for marking APIs that are not supported in specific environments
    • Can be applied to methods, constructors, classes, and properties
    • Supports multiple attributes on the same target for different environments
    • Requires non-null environmentId and reason parameters

Analyzer (NServiceBus.Core.Analyzer)

  • DiagnosticIds.cs: Added NSBENV001 constant
  • NotSupportedInEnvironmentAnalyzer.cs: New analyzer that:
    • Reads environment configuration from build_property.NServiceBusEnvironment MSBuild property
    • Analyzes method invocations, object creations, and property references
    • Reports warning when API marked with [NotSupportedInEnvironment] is used in the configured environment
    • Supports method-level attributes overriding type-level attributes
    • Handles multiple attributes on the same target

Usage

To enable the analyzer for a specific environment, add to your .csproj:

<PropertyGroup>
  <NServiceBusEnvironment>AzureFunctionsIsolated</NServiceBusEnvironment>
</PropertyGroup>

The analyzer will then warn when using APIs marked as not supported in that environment:

// This will trigger NSBENV001 warning in Azure Functions isolated environment
var endpoint = await Endpoint.Start(configuration);

Transitive Package Support

The MSBuild property can be set transitively by downstream packages. For example, the Azure Functions package can automatically set this property:

<!-- In NServiceBus.AzureFunctions.Worker.ServiceBus.targets -->
<PropertyGroup>
  <NServiceBusEnvironment>Serverless</NServiceBusEnvironment>
</PropertyGroup>

This allows environment-specific packages to automatically enable the appropriate analyzer configuration without requiring users to manually configure it.

Centralized Enforcement

Organizations can centrally enforce the analyzer across all projects in their solution by setting the property in a Directory.Build.props file:

<!-- In Directory.Build.props -->
<PropertyGroup>
  <NServiceBusEnvironment>Serverless</NServiceBusEnvironment>
</PropertyGroup>

This ensures all projects in the solution are analyzed for the target environment without requiring individual project configuration.

Known Limitations

The analyzer analyzes direct API invocations at compile time. It will not detect usage through configuration abstraction methods defined in external libraries (e.g., a library providing helper methods that internally call forbidden APIs). This is not considered a significant limitation given modern composition patterns using the host builder APIs where endpoint configuration is typically done directly in the application code rather than through library abstractions, and the possibility to enforce it via MSBuild property centralization techniques like Directory.Build.props.

Backwards Compatibility

  • No breaking changes
  • New APIs are additive only
  • Analyzer is opt-in via MSBuild property

@danielmarbach
Copy link
Contributor Author

Some of the analyzer test base changes will clash with #7597 but we'll figure it out.

Comment on lines +211 to +212
[NotSupportedInEnvironment("AzureFunctionsIsolated", "Not in Azure Functions")]
[NotSupportedInEnvironment("LambdaEnvironment", "Not in Lambda")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be too fancy to support wildcards? Ie to have ServerLess here and then have lambda define ServerLess.Lambda ?

@danielmarbach
Copy link
Contributor Author

danielmarbach commented Feb 6, 2026

An alternative approach worth considering is a category-based model instead of (or in addition to) explicit environment checks.

APIs could be annotated with something like an ApiCategory attribute (for example Receive, Send, EndpointLifecycle, Performance, etc.), and downstream packages or applications could opt out of entire categories via configuration. This shifts the model from "this API is not supported here" to "this profile does not expose this class of capabilities".

That has a couple of advantages:

  • Downstream integrations can define their own profiles without needing new environment identifiers in core.
  • Categories tend to be more stable and expressive than environment names.
  • It opens the door to making certain APIs effectively “unavailable” (for example, via analyzer errors or IntelliSense hiding), not just discouraged.

This is not necessarily an either/or choice. A hybrid model where environments map to disallowed categories could also work well, but even a pure category-based approach could cover many of the same use cases with less environment-specific coupling.

@andreasohlund
Copy link
Member

An alternative approach worth considering is a category-based model instead of (or in addition to) explicit environment checks.

I had similar thoughts when we talked about the "route to endpoint instance" APIs thing 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants