I've been using MassTransit for handling e-mail messages. Using this code: http://meandaspnet.blogspot.com/2009/10/how-to-binary-serialize-mailmessage-for.html I'm able to binary serialize my e-mails and publish them to my service bus. They're handled correctly too.
Bus.Initialize(
sbc =>
{
sbc.EnableMessageTracing();
sbc.ReceiveFrom("msmq://localhost/MyQueue");
sbc.UseMsmq();
sbc.VerifyMsmqConfiguration();
sbc.UseMulticastSubscriptionClient();
sbc.UseBinarySerializer();
sbc.Subscribe(subs => subs.Instance(new MessageHandler()));
});
Now I added a new type and handler:
// Check out the sequence of the Consumes<> !
public class MessageHandler :
Consumes<SerializeableMailMessage>.All,
Consumes<AangifteOmzetbelasting>.All
{
public void Consume(AangifteOmzetbelasting message)
{
// Some code - method will NOT be called
}
public void Consume(SerializeableMailMessage mailMessage)
{
// Some code - this method is called by Mass Transit
}
}
The weird thing is that this works if I Publish a SerializableMailMessage - but not for AangifteOmzetbelasting. If I change the interface order - it works for AangifteOmzetbelasting and not for SerializableMailMessage. Like so:
// Check out the sequence of the Consumes<> !
public class MessageHandler :
Consumes<AangifteOmzetbelasting>.All,
Consumes<SerializeableMailMessage>.All
In the latter case, the SerializedMailMessges do not appear on the service bus either. Both are published using:
Bus.Instance.Publish(object)
What am I doing wrong here?
Publishing messages without type information is a real struggle; the type information is hugely important for routing.
What I would look at doing here, if you must publish as object, is we have FastActivator helpers you can take a peek at (should be in the referenced Magnum library) that would be like Bus.Instance.FastActivator("Publish", message, { message.GetType() }). I might have the order of the parameters wrong, but you need the method name, parameters, and generic type parameters.
Additionally, I'd suggest joining the MT mailing list to help with this issue further if you need it. https://groups.google.com/forum/#!forum/masstransit-discuss
Related
We use SignalR in several applications – it’s very handy and works well! :-) We usually have a C# server side and a JavaScript client.
But now we have a “special need”: On the server side, we would like to have one and the same method executed regardless of what message type the client sends. A kind of “catch-all method” in the SignalR server. (It’s for a special testing purpose – there will be added new message types all the time. The message parameter is always an object.)
Is this possible? I know about the HubMethodNameAttribute, and I basically would like to be able to use some sort of wildcard there. Something equal to this in the hub class:
[HubMethodName("*")]
public Task MyCatchAll(object par)
{
// handle the message
}
Or some other way to always get the same method called, regardless of message type.
We don’t want to have to maintain a list of all possible messages, like this:
public Task OneOfTheMessageTypes(object par) => MyCatchAll(par);
public Task AnotherMessageType(object par) => MyCatchAll(par);
public Task AndYetAnotherOne(object par) => MyCatchAll(par);
public Task AndSoOn(object par) => MyCatchAll(par);
...
(I’ve tried using an IHubFilter, but that isn’t called until SignalR has identified the method to call; can’t be used for message types not defined.)
I’d be most grateful for some help from a SignalR expert! :-)
/Anders from Sweden
I have a MassTransit routing slip with a couple of activities that works perfectly (I love MT) but I now want to add additional information to the failed events (i.e. error name and description).
At the moment I catch my custom exceptions in the activity and send back a faulted response, which kicks in all of the compensating activites as planned, but I can't seem to get the exception details in the subscriber (to add to the event I then send back to the saga).
My activity looks like this:
public async Task<ExecutionResult> Execute(ExecuteContext<ICreateLink> context)
{
var messageCommand = context.Arguments;
var command = new CreateLink(
messageCommand.LinkId,
messageCommand.GroupId);
try
{
await _commandDispatcher.ExecuteAsync(command).ConfigureAwait(false);
return context.Completed();
}
catch (Exception ex)
{
return context.Faulted(ex);
}
}
Then when building the routing slip I have:
builder.AddSubscription(
context.SourceAddress,
RoutingSlipEvents.ActivityFaulted,
RoutingSlipEventContents.All,
nameof(CreateLinkActivity),
x => x.Send<ICreateLinkFailed>(new CreateLinkFailed
{
LinkId = context.Message.LinkId,
LinkName = context.Message.Name
}));
I thought I would be able to access the exception information from the context but alas I can't seem to find it and this is the last piece of the puzzle for me.
I'm beginning to think that I'm not thinking about this right. Ultimately I want to pass the error type back to the routing slip and then to it's calling saga.
With your subscription, your event type can include properties with the same type/name as the built-in event that is published. Those properties will be added by MT to the event automatically (no need to map them in the Send call).
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit/Courier/Contracts/RoutingSlipActivityFaulted.cs#L19
So in your case, the ExceptionInfo property could be copied into your event interface - and that data will be presented when the event is consumed.
Under the hood, MassTransit merges the JSON of the built-in event and your own event assignment into a combined JSON document.
I am working with code that I have not written and am trying to understand why a particular piece of functionality is not working the way I intend. In particular, I have three projects within my solution:
API
Messages
Events
Processors
Events is a folder within Messages. API will communicate with Processors using NServiceBus via messages defined in Messages/Events. Each of the classes in Messages/Events extends IMessage.
Now within a configuration file for the bus that is shared by both Processors and API, I found these lines:
var conventionsBuilder = config.Conventions();
conventionsBuilder.DefiningEventsAs(t => t.Namespace != null && t.Namespace.StartsWith("DE.STEP.Messages") && t.Namespace.EndsWith("Events"));
return config;
This was implemented earlier, and the classes inside Messages/Events did NOT extend IMessage at an earlier time, instead the above code defined where and what a message could be qualified as. Now, since I have introduced IMessage, I figured I could remove those 3 lines of code. However, when I do, no messages ever make it to my queue from API. The code that attempts this publish is Bus.Publish<>();
How should I configure this so that I do not need to have a hard-coded string referencing the assembly where the messages are? I want my code to scan the solution for anything that extends IMessage and treat it as something that can be published and can be handled.
EDIT: As per the NServiceBus docs
By default, NServiceBus scans all assemblies in the endpoint
bin folder to find types implementing its interfaces so that
it can configure them automatically.
And Messages.dll appears in the bin of both API and Processors. So since Messages.dll contains all of my IMessages, shouldn't the default behavior suffice to make them available for publishing / subscribing? I.e. shouldn't removing the 3 lines in question have no effect?
The hierarchy of an event, if you're not using unobtrusive configuration, should be as follows:
ConcreteEvent : IEvent : IMessage
The reason for this is, after having had a look at the source code, your message endpoint mappings are processed as follows:
foreach (var mapping in messageEndpointMappings)
{
mapping.Configure((messageType, address) =>
{
var conventions = context.Settings.Get<Conventions>();
if (!(conventions.IsMessageType(messageType) || conventions.IsEventType(messageType) || conventions.IsCommandType(messageType)))
{
return;
}
if (conventions.IsEventType(messageType))
{
router.RegisterEventRoute(messageType, address);
return;
}
router.RegisterMessageRoute(messageType, address);
});
}
Conventions.IsEventType is a lookup implemented as follows:
EventsConventionCache.ApplyConvention(t, type => IsEventTypeAction(type));
I won't give you the full call stack but basically, you end up here:
t => typeof(IEvent).IsAssignableFrom(t) && typeof(IEvent) != t
This will evaluate as false for a concrete event only implementing IMessage, so RegisterEventRoute is never called for your event but rather it'll be treated as an IMessage, which follows different routing rules (only one route per IMessage).
In short, you must have IEvent interface on your event class to be able to use IBus.Publish if you are not using unobtrusive configuration.
I am trying to design client/server application, that would be able to exchange "commands". The thing is, that server application is processing some stuff and I would like the client to be able to for example send command "pause" to the server.
The thing is, that my manager suggested, that best approach would be to create interface (ICommand for example) and then class for each command (Pause, Resume) that would inherit from the ICommand. After that, we could simply create an object Pause with [DataContract] attribute, that would be sent over to server.
For that purpouse, I tried to use shared-types, so I created seprated assembly in which I designed all the [DataContracts], so that both server and client can use them (they have reference leading to them).
On the server, we would then have [OperationContract], that would take the [DataContract] as parameter and return [DataContract] as well, like this:
[ServiceKnownType(typeof(PauseServer))]
[ServiceKnownType(typeof(Resume))]
[ServiceContract]
public interface ITestService
{
[OperationContract]
ICommand DoCommand(ICommand command);
}
The problem is, that apart from some properties, we would like to have for example method "Execute(param1,param2)", that would do certain operation - this method would do different operation on server (pause the process) and different operation on client side (change the status and enable "Resume" button for example). Like this:
[DataContract(Namespace="PauseContract")]
public class Pause
{
string _param1;
int _param2;
public void Execute()
{
// DO SOMETHING HERE
}
[DataMember]
public string Param1
{
get
{
return _param1;
}
set
{
this._param1 = value;
}
}
[DataMember]
public int Param2
{
get
{
return _param2;
}
set
{
this._param2 = value;
}
}
}
In the end, the whole process would like this:
1) Client wants to pause the process, so it creates object "Pause", that would contain for example ID of the process.
2) This object is passed to the DoCommand() method, which creates object "Pause" on server side and run its "Execute()" method with the given parameters.
3) If the Pausing process ended well, the Pause object is returned back to client (with process ID and other attributes)
4) If client gets this response, it will know that the process has eben paused and run its own "Execute()" method on its own Pause object, that would change the GUI and so on.
So my question is - is it somehow possible, to have different implementation of contracts stored in common library on both server/client side? Or is this approach wrong in general? From what I have heards, it is not advised to include behaviour (methods) to [DataContracts], but I thought it would be ok, if I dont mark them with [DataMember] attribute.
Thank You, Jakub.
To be honest, I don't think the ICommand with ServiceKnownType attribute idea works well for commands.
ServiceKnownType is designed to support polymorphism across service boundaries in the context of type properties and not behavior.
Your Pause/Resume scenario would be very easily implement with the exchange of two distinct request/response DataContract definitions.
My infrastructure:
Main - ServiceStack self hosted console app. 'Main' sends messages to MQ.
Background - ServiceStack self hosted console app. 'Background' receives messages from MQ.
Locally installed Redis
In 'Main' AppHost I configure Redis manager:
container.Register<IRedisClientsManager>(
new PooledRedisClientManager("localhost:6379"));
Then I run this code somewhere in service:
using (var client = new RedisMessageQueueClient(TryResolve<IRedisClientsManager>()))
{
client.Publish(new TestMessage { Value = "From ping" });
}
Everything works great and I can get message in my 'Background'. But problem comes when I wrap this code in class:
public class MessageQueuePublisher : IMessageQueuePublisher
{
public void Publish(object message)
{
using (var client = new RedisMessageQueueClient(
EndpointHost.AppHost.TryResolve<IRedisClientsManager>()))
{
client.Publish(message);
}
}
}
When I call MessageQueuePublisher.Publish method from the exactly same place where previous code was executed, it seems like it works correctly (no exceptions are thrown), but my message doesn't reach 'Background'.
Is this OK?
I found a solution. On my 'Background' I expect message with type TestMessage
mqService.RegisterHandler<TestMessage>(ServiceController.ExecuteMessage);
But when using MessageQueuePublisher.Publish message was of type object and went to the object queue and wasn't handled.
So to solve this problem Publish method should be generic:
public void Publish<T>(T message)
It doesn't change how method is called but code is not so good because if you look at it, it's not clear why generic is used. But at least it works.