Conventions

Component: NServiceBus
NuGet Package NServiceBus (8-pre)
This page targets a pre-release version and is subject to change prior to the final release.

A convention is a way of defining what a certain type is instead of using an interface or an attribute. Using conventions along with avoiding references to NServiceBus assemblies is referred to as unobtrusive mode. This is ideal for use in cross-platform environments.

Currently conventions exist to identify:

Messages can be defined in a Portable Class Library (PCL) and shared across multiple platforms even if the platform does not use NServiceBus for message processing.

var conventions = endpointConfiguration.Conventions();
conventions.DefiningCommandsAs(type => type.Namespace == "MyNamespace.Messages.Commands");
conventions.DefiningEventsAs(type => type.Namespace == "MyNamespace.Messages.Events");
conventions.DefiningMessagesAs(type => type.Namespace == "MyNamespace.Messages");
conventions.DefiningDataBusPropertiesAs(property => property.Name.EndsWith("DataBus"));
conventions.DefiningExpressMessagesAs(type => type.Name.EndsWith("Express"));
conventions.DefiningTimeToBeReceivedAs(type => type.Name.EndsWith("Expires") ? TimeSpan.FromSeconds(30) : TimeSpan.MaxValue);
Note that in .NET, the namespace is optional and hence can be null. If any conventions do partial string checks, for example using EndsWith or StartsWith, then a null check should be used. So include .Namespace != null at the start of the convention. Otherwise a null reference exception will occur during the type scanning.

Using both default and custom conventions

Defining conventions will replace any of the existing conventions. If both default and custom conventions are needed it is required to embed the NServiceBus marker interfaces/types into the new custom conventions.

var conventions = endpointConfiguration.Conventions();
conventions.DefiningCommandsAs(type =>
    type.Namespace == "MyNamespace.Messages.Commands"
    || typeof(ICommand).IsAssignableFrom(type)
);
conventions.DefiningEventsAs(type =>
    type.Namespace == "MyNamespace.Messages.Events"
    || typeof(IEvent).IsAssignableFrom(type)
);
conventions.DefiningMessagesAs(type =>
    type.Namespace == "MyNamespace.Messages"
    || typeof(IMessage).IsAssignableFrom(type)
);
conventions.DefiningDataBusPropertiesAs(property =>
    property.Name.EndsWith("DataBus")
    || typeof(IDataBusProperty).IsAssignableFrom(property.PropertyType) && typeof(IDataBusProperty) != property.PropertyType
);
conventions.DefiningTimeToBeReceivedAs(type =>
type.Name.EndsWith("Expires")
        ? TimeSpan.FromSeconds(30)
        : type.GetCustomAttribute<TimeToBeReceivedAttribute>(false)?.TimeToBeReceived ?? TimeSpan.MaxValue
);

Encapsulated conventions

Messaging conventions can be encapsulated into a class.

class MyNamespaceMessageConvention : IMessageConvention
{
    public bool IsMessageType(Type type) => type.Namespace == "MyNamespace.Messages";
    public bool IsCommandType(Type type) => type.Namespace == "MyNamespace.Messages.Events";
    public bool IsEventType(Type type) => type.Namespace == "MyNamespace.Messages.Commands";
    public string Name { get; } = "MyNamespace message convention";
}

These conventions can be added to the endpoint.

endpointConfiguration.Conventions().Add(new MyNamespaceMessageConvention());

Multiple encapsulated conventions can be applied to the same endpoint. A class will be a considered a message, command, or event if any convention matches it.

Adding an instance of IMessageConvention does not override the default conventions.

Attributes

If attributes are preferred over marker interfaces then this can be achieved via NServiceBus.AttributeConventions, a community package that allows using attributes instead of interfaces.

Related Articles


Last modified