Note
This option is available only for the publisher with return value.
Publisher is the component which publish message to all subscribers.
Uses RegisterPublisher method of the MessageBus instance to register a publisher.
RegisterPublisher: Register a publisher without return value. No return value is collected while this publisher executing.
RegisterPublisher: Register a publisher with return value supported. Return value is transferred back to the publisher after executed.
Message name is required when registering publisher. The name is used for matching subscribers.
To modify the behaviour of the publisher, an instance of MessageBusPublisherOptions(MessageBusPublisherOptionsTParameter or MessageBusPublisherOptionsTParameter, TReturn) can be specified. Default behavior is used when no option supplied. See Option block for details.
An instance of PublisherTicket (PublisherTicketTParameter or PublisherTicketTParameter, TReturn) is returned after RegisterPublisher called, containing an id for identifying, an executor for running publisher and the instance of the option provided with registering if presents.
Publisher can be unregistered when no longer needed. Calls UnregisterPublisher method to unregister the publisher by the ticket or id specified.
There is a property named Executor in PublisherTicket which contains 4 methods to execute the sequence. For detailed executing sequence, refer to Executing.
For the publisher registered with return value, property Executor contains 4 methods:
Execute: Executes the sequence and get the return value.
ExecuteAndGetMessageInstance: Executes the sequence and get the return value with the instance information.
ExecuteAsync: Asynchronously executes the sequence and get the return value.
ExecuteAndGetMessageInstanceAsync: Asynchronously executes the sequence and get the return value with the instance information.
When no acceptable return value is generated from any subscriber, the default value is returned if presents.
For the publisher registered without return value, property Executor contains 4 methods:
Execute: Executes the sequence.
ExecuteAndGetMessageInstance: Executes the sequence and get the instance information.
ExecuteAndGetMessageInstanceAsync: Asynchronously executes the sequence.
ExecuteAsync: Asynchronously executes the sequence and get the instance information.
When executes by synchronous methods: Synchronous methods are run directly. Each asynchronous method is called with Task.Wait().
When executes by asynchronous methods: Synchronous methods are run directly. Each asynchronous method is called with await keyword.
public async Task TestMethodAsync()
{
using var bus = new MessageBus();
var subscriber1000Ticket = bus.RegisterSubscriber<int, int>("ArgumentMatching", Subscriber1000,
new MessageBusSubscriberOptions<int, int>(sequence: 0, resultCheckingCallback: i => i > 0));
var subscriber100Ticket = bus.RegisterSubscriber<int, int>("ArgumentMatching", Subscriber100Async,
new MessageBusSubscriberOptions<int, int>(sequence: 1, resultCheckingCallback: i => i > 0));
var publisherTicket = bus.RegisterPublisher<int, int>("ArgumentMatching", new MessageBusPublisherOptions<int, int>(defaultReturnValue: 5));
var result1000 = publisherTicket.Executor.Execute(1500);
var result100 = publisherTicket.Executor.Execute(300);
var result5 = publisherTicket.Executor.Execute(80);
var result1000Async = await publisherTicket.Executor.ExecuteAsync(1500);
using var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.Cancel();
Exception e100Async = null!;
try
{
var result100Async = await publisherTicket.Executor.ExecuteAsync(300, cancellationTokenSource.Token);
}
catch (Exception e)
{
e100Async = e;
}
var result5BAsync = await publisherTicket.Executor.ExecuteAsync(80);
//after being executed: result1000 == 1000, result100 == 100, result5 == 5, e100Async is OperationCanceledException, result5BAsync == 5
bus.UnregisterPublisher(publisherTicket);
bus.UnregisterSubscriber(subscriber1000Ticket);
bus.UnregisterSubscriber(subscriber100Ticket);
}
public int Subscriber1000(int argument)
{
if (argument > 1000)
return 1000;
else
return 0;
}
public async Task<int> Subscriber100Async(int argument, CancellationToken cancellationToken)
{
await Task.Delay(100, cancellationToken);
if (argument > 100)
return 100;
else
return 0;
}
Async Function TestMethod() As Task
Using bus As New MessageBus
Dim subscriber1000Ticket = bus.RegisterSubscriber(Of Integer, Integer)("ArgumentMatching", AddressOf Subscriber1000, New MessageBusSubscriberOptions(Of Integer, Integer)(sequence:=0, resultCheckingCallback:=Function(i) i > 0))
Dim subscriber100Ticket = bus.RegisterSubscriber(Of Integer, Integer)("ArgumentMatching", AddressOf Subscriber100Async, New MessageBusSubscriberOptions(Of Integer, Integer)(sequence:=1, resultCheckingCallback:=Function(i) i > 0))
Dim publisherTicket = bus.RegisterPublisher(Of Integer, Integer)("ArgumentMatching", New MessageBusPublisherOptions(Of Integer, Integer)(defaultReturnValue:=5))
Dim result1000 = publisherTicket.Executor.Execute(1500)
Dim result100 = publisherTicket.Executor.Execute(300)
Dim result5 = publisherTicket.Executor.Execute(80)
Dim result1000Async = Await publisherTicket.Executor.ExecuteAsync(1500)
Dim e100Async As Exception = Nothing
Using cancellationTokenSource = New CancellationTokenSource()
cancellationTokenSource.Cancel()
Try
Dim result100Async = Await publisherTicket.Executor.ExecuteAsync(300, cancellationTokenSource.Token)
Catch ex As Exception
e100Async = ex
End Try
End Using
Dim result5BAsync = Await publisherTicket.Executor.ExecuteAsync(80)
'after being executed: result1000 = 1000, result100 = 100, result5 = 5, e100Async is OperationCanceledException, result5BAsync = 5
bus.UnregisterPublisher(publisherTicket)
bus.UnregisterSubscriber(subscriber1000Ticket)
bus.UnregisterSubscriber(subscriber100Ticket)
End Using
End Function
Public Function Subscriber1000(argument As Integer) As Integer
If argument > 1000 Then
Return 1000
Else
Return 0
End If
End Function
Public Async Function Subscriber100Async(argument As Integer, cancellationToken As CancellationToken) As Task(Of Integer)
Await Task.Delay(100, cancellationToken)
If argument > 100 Then
Return 100
Else
Return 0
End If
End Function
By creating publisher ticket and run from the executor, reusable information is saved for multiple calling but saving the costs time and memory. ExecuteOnce methods provide easier and more effective way to run the message which only need to be run once by design.
ExecuteOnce methods includes:
These methods are similar to ones defined in Executor with message name specified.
To modify the behaviour of the publisher, specifies an instance of MessageBusPublisherOptions(MessageBusPublisherOptionsTParameter or MessageBusPublisherOptionsTParameter, TReturn) while registering.
When IsAlwaysExecuteAll is set, all subscribers linked with this message name should be executed regardless of the result of the subscribers those have been executed by this instance.
public void TestMethod()
{
using var bus = new MessageBus();
var subscriberExact1Ticket = bus.RegisterSubscriber<string>("Hello", SubscriberExact1);
var subscriberExact2Ticket = bus.RegisterSubscriber<string>("hello", SubscriberExact2);
var subscriberIgnoreCaseTicket = bus.RegisterSubscriber<string>(new MessageNameMatchingWithStringComparison("HELLO", StringComparison.OrdinalIgnoreCase), SubscriberIgnoreCase);
var subscriberAllTicket = bus.RegisterSubscriber<string>(new MessageNameMatchingAll(), SubscriberAll);
var subscriberRegEx1Ticket = bus.RegisterSubscriber<string>(new MessageNameMatchingWithRegularExpression("[Hh]ello"), SubscriberRegEx1);
var subscriberRegEx2Ticket = bus.RegisterSubscriber<string>(new MessageNameMatchingWithRegularExpression("hello"), SubscriberRegEx2);
var text = "Hello World";
var publisherTicket = bus.RegisterPublisher<string>("Hello", new MessageBusPublisherOptions<string>(true));
publisherTicket.Executor.Execute(text);
bus.UnregisterPublisher(publisherTicket);
bus.UnregisterSubscriber(subscriberExact1Ticket);
bus.UnregisterSubscriber(subscriberExact2Ticket);
bus.UnregisterSubscriber(subscriberIgnoreCaseTicket);
bus.UnregisterSubscriber(subscriberAllTicket);
bus.UnregisterSubscriber(subscriberRegEx1Ticket);
bus.UnregisterSubscriber(subscriberRegEx2Ticket);
//after being executed: _exact1, _ignoreCase, _all and _regex1 are equal to text; _exact2 and _regex2 are not.
}
private string? _exact1, _exact2, _ignoreCase, _all, _regex1 , _regex2;
public void SubscriberExact1(string? argument)
{
_exact1 = argument;
}
public void SubscriberExact2(string? argument)
{
_exact2 = argument;
}
public void SubscriberIgnoreCase(string? argument)
{
_ignoreCase = argument;
}
public void SubscriberAll(string? argument)
{
_all = argument;
}
public void SubscriberRegEx1(string? argument)
{
_regex1 = argument;
}
public void SubscriberRegEx2(string? argument)
{
_regex2 = argument;
}
Sub TestMethod()
Using bus As New MessageBus
Dim subscriberExact1Ticket = bus.RegisterSubscriber(Of String)("Hello", AddressOf subscriberExact1)
Dim subscriberExact2Ticket = bus.RegisterSubscriber(Of String)("hello", AddressOf SubscriberExact2)
Dim subscriberIgnoreCaseTicket = bus.RegisterSubscriber(Of String)(New MessageNameMatchingWithStringComparison("HELLO", StringComparison.OrdinalIgnoreCase), AddressOf SubscriberIgnoreCase)
Dim subscriberAllTicket = bus.RegisterSubscriber(Of String)(New MessageNameMatchingAll, AddressOf SubscriberAll)
Dim subscriberRegEx1Ticket = bus.RegisterSubscriber(Of String)(New MessageNameMatchingWithRegularExpression("[Hh]ello"), AddressOf SubscriberRegEx1)
Dim subscriberRegEx2Ticket = bus.RegisterSubscriber(Of String)(New MessageNameMatchingWithRegularExpression("hello"), AddressOf SubscriberRegEx2)
Dim text = "Hello World"
Dim publisherTicket = bus.RegisterPublisher(Of String)("Hello", New MessageBusPublisherOptions(Of String)(True))
publisherTicket.Executor.Execute(text)
bus.UnregisterPublisher(publisherTicket)
bus.UnregisterSubscriber(subscriberExact1Ticket)
bus.UnregisterSubscriber(subscriberExact2Ticket)
bus.UnregisterSubscriber(subscriberIgnoreCaseTicket)
bus.UnregisterSubscriber(subscriberAllTicket)
bus.UnregisterSubscriber(subscriberRegEx1Ticket)
bus.UnregisterSubscriber(subscriberRegEx2Ticket)
'after being executed: _exact1, _ignoreCase, _all and _regex1 are equal to text; _exact2 and _regex2 are not.
End Using
End Sub
Private _exact1, _exact2, _ignoreCase, _all, _regex1, _regex2 As String
Public Sub SubscriberExact1(argument As String)
_exact1 = argument
End Sub
Public Sub SubscriberExact2(argument As String)
_exact2 = argument
End Sub
Public Sub SubscriberIgnoreCase(argument As String)
_ignoreCase = argument
End Sub
Public Sub SubscriberAll(argument As String)
_all = argument
End Sub
Public Sub SubscriberRegEx1(argument As String)
_regex1 = argument
End Sub
Public Sub SubscriberRegEx2(argument As String)
_regex2 = argument
End Sub
In the demo above, the publisher is registered with IsAlwaysExecuteAll.
If ArgumentConvertingCallback is specified, the callback is invoked to convert the argument the when publisher executing. See executing sequence for details.
public void TestMethod()
{
using var bus = new MessageBus();
var subscriber1000Ticket = bus.RegisterSubscriber<int, int>("ArgumentMatching", Subscriber1000,
new MessageBusSubscriberOptions<int, int>(sequence: 0, resultCheckingCallback: i => i > 0));
var subscriber100Ticket = bus.RegisterSubscriber<int, int>("ArgumentMatching", Subscriber100,
new MessageBusSubscriberOptions<int, int>(sequence: 1, resultCheckingCallback: i => i > 0));
var publisherTicket = bus.RegisterPublisher<string, string>("ArgumentMatching",
new MessageBusPublisherOptions<string, string>(
defaultReturnValue: "5",
argumentConvertingCallback: s => int.Parse(s!),
returnValueConvertingCallback: i => i?.ToString()));
var result1000 = publisherTicket.Executor.Execute("1500");
var result100 = publisherTicket.Executor.Execute("300");
var result5 = publisherTicket.Executor.Execute("80");
//after being executed: result1000 == 1000, result 100 == 100, result5 == 5
bus.UnregisterPublisher(publisherTicket);
bus.UnregisterSubscriber(subscriber1000Ticket);
bus.UnregisterSubscriber(subscriber100Ticket);
}
public int Subscriber1000(int argument)
{
if (argument > 1000)
return 1000;
else
return 0;
}
public int Subscriber100(int argument)
{
if (argument > 100)
return 100;
else
return 0;
}
Public Sub TestMethod()
Using bus As New MessageBus
Dim subscriber1000Ticket= bus.RegisterSubscriber(Of Integer, Integer)("ArgumentMatching", AddressOf Subscriber1000, New MessageBusSubscriberOptions(Of Integer, Integer)(sequence:=0, resultCheckingCallback:=Function(i) i > 0))
Dim subscriber100Ticket = bus.RegisterSubscriber(Of Integer, Integer)("ArgumentMatching", AddressOf Subscriber100, New MessageBusSubscriberOptions(Of Integer, Integer)(sequence:=1, resultCheckingCallback:=Function(i) i > 0))
Dim publisherTicket = bus.RegisterPublisher(Of String, String)("ArgumentMatching", New MessageBusPublisherOptions(Of String, String)(defaultReturnValue:="5", argumentConvertingCallback:=Function(s) Integer.Parse(s), returnValueConvertingCallback:=Function(i) i.ToString()))
Dim result1000 = publisherTicket.Executor.Execute("1500")
Dim result100 = publisherTicket.Executor.Execute("300")
Dim result5 = publisherTicket.Executor.Execute("80")
'after being executed: result1000 = 1000, result100 = 100, result5 = 5
bus.UnregisterPublisher(publisherTicket)
bus.UnregisterSubscriber(subscriber1000Ticket)
bus.UnregisterSubscriber(subscriber100Ticket)
End Using
End Sub
Public Function Subscriber1000(argument As Integer) As Integer
If argument > 1000 Then
Return 1000
Else
Return 0
End If
End Function
Public Function Subscriber100(argument As Integer) As Integer
If argument > 100 Then
Return 100
Else
Return 0
End If
End Function
In the demo above, the publisher is registered with ArgumentConvertingCallback specified.
var publisherTicket = bus.RegisterPublisher<string, string>("ArgumentMatching",
new MessageBusPublisherOptions<string, string>(
defaultReturnValue: "5",
argumentConvertingCallback: s => int.Parse(s!),
returnValueConvertingCallback: i => i?.ToString()));
Dim publisherTicket = bus.RegisterPublisher(Of String, String)("ArgumentMatching", New MessageBusPublisherOptions(Of String, String)(defaultReturnValue:="5", argumentConvertingCallback:=Function(s) Integer.Parse(s), returnValueConvertingCallback:=Function(i) i.ToString()))
If ReturnValueConvertingCallback is specified, the callback is invoked to convert the return value before publisher returning. See executing sequence for details.
public void TestMethod()
{
using var bus = new MessageBus();
var subscriber1000Ticket = bus.RegisterSubscriber<int, int>("ArgumentMatching", Subscriber1000,
new MessageBusSubscriberOptions<int, int>(sequence: 0, resultCheckingCallback: i => i > 0));
var subscriber100Ticket = bus.RegisterSubscriber<int, int>("ArgumentMatching", Subscriber100,
new MessageBusSubscriberOptions<int, int>(sequence: 1, resultCheckingCallback: i => i > 0));
var publisherTicket = bus.RegisterPublisher<string, string>("ArgumentMatching",
new MessageBusPublisherOptions<string, string>(
defaultReturnValue: "5",
argumentConvertingCallback: s => int.Parse(s!),
returnValueConvertingCallback: i => i?.ToString()));
var result1000 = publisherTicket.Executor.Execute("1500");
var result100 = publisherTicket.Executor.Execute("300");
var result5 = publisherTicket.Executor.Execute("80");
//after being executed: result1000 == 1000, result 100 == 100, result5 == 5
bus.UnregisterPublisher(publisherTicket);
bus.UnregisterSubscriber(subscriber1000Ticket);
bus.UnregisterSubscriber(subscriber100Ticket);
}
public int Subscriber1000(int argument)
{
if (argument > 1000)
return 1000;
else
return 0;
}
public int Subscriber100(int argument)
{
if (argument > 100)
return 100;
else
return 0;
}
Public Sub TestMethod()
Using bus As New MessageBus
Dim subscriber1000Ticket= bus.RegisterSubscriber(Of Integer, Integer)("ArgumentMatching", AddressOf Subscriber1000, New MessageBusSubscriberOptions(Of Integer, Integer)(sequence:=0, resultCheckingCallback:=Function(i) i > 0))
Dim subscriber100Ticket = bus.RegisterSubscriber(Of Integer, Integer)("ArgumentMatching", AddressOf Subscriber100, New MessageBusSubscriberOptions(Of Integer, Integer)(sequence:=1, resultCheckingCallback:=Function(i) i > 0))
Dim publisherTicket = bus.RegisterPublisher(Of String, String)("ArgumentMatching", New MessageBusPublisherOptions(Of String, String)(defaultReturnValue:="5", argumentConvertingCallback:=Function(s) Integer.Parse(s), returnValueConvertingCallback:=Function(i) i.ToString()))
Dim result1000 = publisherTicket.Executor.Execute("1500")
Dim result100 = publisherTicket.Executor.Execute("300")
Dim result5 = publisherTicket.Executor.Execute("80")
'after being executed: result1000 = 1000, result100 = 100, result5 = 5
bus.UnregisterPublisher(publisherTicket)
bus.UnregisterSubscriber(subscriber1000Ticket)
bus.UnregisterSubscriber(subscriber100Ticket)
End Using
End Sub
Public Function Subscriber1000(argument As Integer) As Integer
If argument > 1000 Then
Return 1000
Else
Return 0
End If
End Function
Public Function Subscriber100(argument As Integer) As Integer
If argument > 100 Then
Return 100
Else
Return 0
End If
End Function
In the demo above, the publisher is registered with ReturnValueConvertingCallback specified.
var publisherTicket = bus.RegisterPublisher<string, string>("ArgumentMatching",
new MessageBusPublisherOptions<string, string>(
defaultReturnValue: "5",
argumentConvertingCallback: s => int.Parse(s!),
returnValueConvertingCallback: i => i?.ToString()));
Dim publisherTicket = bus.RegisterPublisher(Of String, String)("ArgumentMatching", New MessageBusPublisherOptions(Of String, String)(defaultReturnValue:="5", argumentConvertingCallback:=Function(s) Integer.Parse(s), returnValueConvertingCallback:=Function(i) i.ToString()))
When DefaultReturnValue is set, the value is returned when no subscriber executed with accepted value returns.
public void TestMethod()
{
using var bus = new MessageBus();
var subscriberHaveReturnTicket = bus.RegisterSubscriber<int, int>("Return", HaveReturn,
new MessageBusSubscriberOptions<int, int>(sequence: 0));
var subscriberNoReturnNoFinalTicket = bus.RegisterSubscriber<int>(new MessageNameMatchingAll(), NoReturn,
new MessageBusSubscriberOptions<int>(sequence: 1, isFinal: false));
var subscriberNoReturnTicket = bus.RegisterSubscriber<int>("NoReturnButFinal", NoReturn,
new MessageBusSubscriberOptions<int>(sequence: 2));
var publisher1Ticket = bus.RegisterPublisher<int, int>("Return", new MessageBusPublisherOptions<int, int>(defaultReturnValue: 100));
var publisher2Ticket = bus.RegisterPublisher<int, int>("SomethingElse", new MessageBusPublisherOptions<int, int>(defaultReturnValue: 100));
var publisher3Ticket = bus.RegisterPublisher<int, int>("NoReturnButFinal", new MessageBusPublisherOptions<int, int>(defaultReturnValue: 100));
_haveReturnExecuted = false;
_noReturnExecuted = false;
var result11 = publisher1Ticket.Executor.Execute(10);
//after being executed: _haveReturnExecuted == true; _noReturnExecuted == false;
_haveReturnExecuted = false;
_noReturnExecuted = false;
var result100 = publisher2Ticket.Executor.ExecuteAndGetMessageInstance(0, out var messageInstance1);
//after being executed: _haveReturnExecuted == false; _noReturnExecuted == true;
_haveReturnExecuted = false;
_noReturnExecuted = false;
var resultDefault = publisher3Ticket.Executor.ExecuteAndGetMessageInstance(0, out var messageInstance2);
//after being executed: _haveReturnExecuted == false; _noReturnExecuted == true;
bus.UnregisterSubscriber(subscriberHaveReturnTicket);
bus.UnregisterSubscriber(subscriberNoReturnNoFinalTicket);
bus.UnregisterSubscriber(subscriberNoReturnTicket);
bus.UnregisterPublisher(publisher1Ticket);
bus.UnregisterPublisher(publisher2Ticket);
bus.UnregisterPublisher(publisher3Ticket);
}
private bool _haveReturnExecuted, _noReturnExecuted;
public int HaveReturn(int something)
{
_haveReturnExecuted = true;
return something + 1;
}
public void NoReturn(int something)
{
_noReturnExecuted = true;
}
Sub TestMethod()
Using bus As New MessageBus
Dim subscriberHaveReturnTicket = bus.RegisterSubscriber(Of Integer, Integer)("Return", AddressOf HaveReturn, New MessageBusSubscriberOptions(Of Integer, Integer)(sequence:=0))
Dim subscriberNoReturnNoFinalTicket = bus.RegisterSubscriber(Of Integer)(New MessageNameMatchingAll(), AddressOf NoReturn, New MessageBusSubscriberOptions(Of Integer)(sequence:=1, isFinal:=False))
Dim subscriberNoReturnTicket = bus.RegisterSubscriber(Of Integer)("NoReturnButFinal", AddressOf NoReturn, New MessageBusSubscriberOptions(Of Integer)(sequence:=2))
Dim publisher1Ticket = bus.RegisterPublisher(Of Integer, Integer)("Return", New MessageBusPublisherOptions(Of Integer, Integer)(defaultReturnValue:=100))
Dim publisher2Ticket = bus.RegisterPublisher(Of Integer, Integer)("SomethingElse", New MessageBusPublisherOptions(Of Integer, Integer)(defaultReturnValue:=100))
Dim publisher3Ticket = bus.RegisterPublisher(Of Integer, Integer)("NoReturnButFinal", New MessageBusPublisherOptions(Of Integer, Integer)(defaultReturnValue:=100))
_haveReturnExecuted = False
_noReturnExecuted = False
Dim result11 = publisher1Ticket.Executor.Execute(10)
'after being executed: _haveReturnExecuted = True, _noReturnExecuted = False
_haveReturnExecuted = False
_noReturnExecuted = False
Dim messageInstance1 As MessageInstance = Nothing
Dim result100 = publisher2Ticket.Executor.ExecuteAndGetMessageInstance(0, messageInstance1)
'after being executed: _haveReturnExecuted = False, _noReturnExecuted = True
_haveReturnExecuted = False
_noReturnExecuted = False
Dim messageInstance2 As MessageInstance = Nothing
Dim resultDefault = publisher3Ticket.Executor.ExecuteAndGetMessageInstance(0, messageInstance2)
'after being executed: _haveReturnExecuted = False, _noReturnExecuted = True
bus.UnregisterSubscriber(subscriberHaveReturnTicket)
bus.UnregisterSubscriber(subscriberNoReturnNoFinalTicket)
bus.UnregisterSubscriber(subscriberNoReturnTicket)
bus.UnregisterPublisher(publisher1Ticket)
bus.UnregisterPublisher(publisher2Ticket)
bus.UnregisterPublisher(publisher3Ticket)
End Using
End Sub
Private _haveReturnExecuted, _noReturnExecuted As Boolean
Public Function HaveReturn(something As Integer) As Integer
_haveReturnExecuted = True
Return something + 1
End Function
Public Sub NoReturn(something As Integer)
_noReturnExecuted = True
End Sub
In the demo above, the executing of publisher2Ticket gets the default return value specified.