So, when I'm configuring the endpoint and I set ep.Consumer<MyConsumer>(context) - am I able to add something to the Consumer that appears in the ConsumeContext on each invocation?
For example something like this:
MyMetaData metadata = new MyMetaData() { foo: "Bar" }
ep.Consumer<MyConsumer>(context, c => c.somehowincludemetadata(metadata));
And then in the Consumer:
public Task Consume(ConsumeContext<Message> context)
{
var metadata = (MyMetaData)context.heresyourmetadata();
}
Obviously that psudeo-code - but what I'm after is essentially a means by which I can add context (like a customer name, computer name) to the context for retrieve during processing.
It's completely on the consumer side, the publisher won't have any idea of the data that needs to be included.
Many thanks.
That metadata typically has to come from someplace, which can be injected as a dependency into the consumer. Another option, if it is based off data in the message, or headers, is to use middleware – such as a scoped filter that could add payload to the ConsumeContext. This option could either set properties on a shared context object scoped in the container, or as mentioned, add payload.
Yet another option is to somehow add an async method that is called before the consumer, such as shown below.
ep.ConfigureConsumer<MyConsumer>(context,
x => x.UseExecute(context => context.Consumer.Property = Value)));
Or, you can add payload as well:
ep.ConfigureConsumer<MyConsumer>(context,
x => x.UseExecute(context =>
context.GetOrAddPayload(() => new MyPayload())));
Related
we need publish multiple event as json string from DB. publish this json event by masstransit like this:
using var scope = _serviceScopeFactory.CreateScope();
var sendEndpointProvider = scope.ServiceProvider.GetService<ISendEndpointProvider>();
var endpoint = await sendEndpointProvider.GetSendEndpoint(new System.Uri("exchange:IntegrationEvents.DynamicEvent:DynamicEvent"))
var json = JsonConvert.SerializeObject(dynamicObject, Newtonsoft.Json.Formatting.None);// sample
var obj = JsonConvert.DeserializeObject(json, new JsonSerializerSettings { });
await endpoint.Send(obj,i=>i.Serializer.ContentType.MediaType= "application/json");
and in config we use this config:
cfg.UseRawJsonSerializer();
when use this config, json event is successful published but we have strange problem : "all" event consumer is called by empty message data ! ... in Rabbitmq jsut published our "Dynamic Event", but in masstrasit all consumers called !!
Thank you for letting us know if we made a mistake
You don't need all of that JSON manipulation, just send the message object using the endpoint with the serializer configured for RawJson. I cover JSON interoperability in this video.
Also, MassTransit does not allow anonymous types to be published. You might be able to publish dynamic or Expando objects.
I used ExpandoObject like this and get this exception "Messages types must not be in the System namespace: System.Dynamic.ExpandoObject" :
dynamic dynamicObject = new ExpandoObject();
dynamicObject.Id = 1;
dynamicObject.Name = "NameForName";
await endpoint.Send(dynamicObject);
and using like this we get same result as "all consumers called":
var dynamicObject = new ExpandoObject() as IDictionary<string, object>;
dynamicObject.Add("Id", 1);
dynamicObject.Add("Name", "NameForName");
I watch your great video, you used from rabbitmq directly .. how "send the message object using the endpoint with the serializer configured for RawJson" in C# code.
Maybe I'm thinking about this wrong, but I'm trying to create a custom attribute for our CMS to handle auth checks.
https://gist.github.com/sitefinitysteve/62ab761256a64a84d8a6#file-sitefinityjwt-cs-L39
So if this service is called from within the CMS from a logged in user, user data is all there for the service method already.
But in the context of being called from an app, the user is technically Anonymous, however I can decode the token and get the user just fine... but not sure how to like pass that over to the service.
Am I just maybe looking at this wrong, and the proper thing to do is to call a CMS API method to just log that person in (seems slow if I already have the persons user object from line 33, and the service context expires instantly.
Use Request.Items Dictionary
You would use the IRequest.Items dictionary for any data you want to pass throughout ServiceStack's Request Pipeline:
//RequestFilter:
req.Items["info"] = new MyRequestInfo { ... };
In Service:
var info = (MyRequestInfo)base.Request.Items["info"];
Have DTO's share common interface
Another option for adding extra info to your Service is to have Request DTO's implement an interfaces, e.g:
public interface IHasInfo
{
MyRequestInfo Info { get; set; }
}
Which you could then populate in your Request Filter, e.g:
((MyRequestInfo)dto).Info = new MyRequestInfo { ... };
Access in Service like any other DTO property, e.g:
public object Any(Request request)
{
var info = request.Info;
}
We use ServiceStack.RabbitMq and I could not find a way to put a custom attribute on the rabbit mq message. I want the publisher to set the attribute on the message and the worker to read it.
A variant is to move that attribute as part of request body but I have a lot of requests and in all honesty the request should not know at all about this kind of information - as that is metadata of the message.
Any idea how this can be achieved?
You can use the Message Filters in RabbitMqServer to add and introspect message properties, e.g:
var mqServer = new RabbitMqServer("localhost")
{
PublishMessageFilter = (queueName, properties, msg) => {
properties.AppId = "app:{0}".Fmt(queueName);
},
GetMessageFilter = (queueName, basicMsg) => {
var props = basicMsg.BasicProperties;
receivedMsgType = props.Type; //automatically added by RabbitMqProducer
receivedMsgApp = props.AppId;
}
};
You could either add the custom attribute to the object you are pushing down the queue or add that attribute to the rabbit message metadata header table. RabbitMQ messages have various metadata attributes that can be set when a message is published.
Check this
I am trying to configure a cluster group router and wanted to sanity check my assumptions on "how" this works.
I have 2 separate nodes in a cluster these have the following roles "mainservice" and "secondservice". Inside the "mainservice" I want to send messages to an Actor within the "secondservice" using a round-robin-group router.
In the akka hocon config I have the following within the akka.actor.deployment section:
/secondserviceproxy {
router = round-robin-group
routees.paths = ["/user/gateway"]
nr-of-instances = 3
cluster {
enabled = on
allow-local-routees = off
use-role = secondservice
}
}
My assumption based on the documentation is that I can create a "secondserviceproxy" actor in the "mainservice" and this handles the routing of messages to any running instances of my "secondservice" on a round-robin basis.
var secondServiceProxy = Context.System.ActorOf(Props.Empty.WithRouter(FromConfig.Instance), "secondserviceproxy");
secondServiceProxy.Tell("Main Service telling me something");
I also made the assumption that the routees.path property means that messages are sent to an Actor in the "secondservice" located in it's Actor hierarchy as follows: "/user/gateway".
Is my working assumption correct? As this implementation is yielding no results in the "secondservice".
Your assumptions are correct. What’s probably happening is that your message is being blasted through the cluster router before the router has had a chance to build its routee table of routees around the cluster (which it builds from monitoring cluster gossip).
Result? Your message initially is ending up in DeadLetters. And then later, once the cluster has fully formed, it will go through because the router knows about its intended recipients around the cluster.
You could verify that if you want by subscribing to dead letters from that actor and checking if that’s where the message is going. You can do that like so:
using Akka.Actor;
using Akka.Event;
namespace Foo {
public class DeadLetterAwareActor : ReceiveActor {
protected ILoggingAdapter Log = Context.GetLogger();
public DeadLetterAwareActor() {
// subscribe to DeadLetters in ActorSystem EventStream
Context.System.EventStream.Subscribe(Self, typeof(DeadLetter));
Receiving();
}
private void Receiving() {
// is it my message being delivered to DeadLetters?
Receive<DeadLetter>(msg => msg.Sender.Equals(Self), msg => {
Log.info("My message to {0} was not delivered :(", msg.Recipient);
})
}
}
}
I am new to Apache camel. I have very common use case that i am struggling to configure camel route. The use case is to take execution context
Update database using execution context.
Then using event on the execution context, create a byte message and send over MQ.
Then in the next step again use execution context and perform event processing.
Update database using execution context.
So basically its kind of nested routes. In the below configuration I need to have access to the executionContext that executionController has created in the updateSchedulerState, sendNotification, processEvent and updateSchedulerState i.e. steps annotated as 1,2, 3 and 4 respectively.
from("direct:processMessage")
.routeId("MessageExecutionRoute")
.beanRef("executionController", "getEvent", true)
.beanRef("executionController", "updateSchedulerState", true) (1)
.beanRef("executionController", "sendNotification", true) (2)
.beanRef("messageTransformer", "transform", true)
.to("wmq:NOTIFICATION")
.beanRef("executionController", "processEvent", true) (3)
.beanRef("eventProcessor", "process", true)
.beanRef("messageTransformer", "transform", true)
.to("wmq:EVENT")
.beanRef("executionController", "updateSchedulerState", true); (4)
Kindly let me know how should i configure the route for the above use case.
Thanks,
Vaibhav
So you need to access this executionContext in your beans at various points in the route?
If I understand correctly, you can put this executionContext in an exchange Property, and it will persist throughout the route.
Setting the exchange property can be done via the Exchange.setProperty() method or various camel dsl functions such as like this:
from("direct:xyz)
.setProperty("awesome", constant("YES"))
//...
You can access exchange properties from a bean by adding a method argument of type Exchange, like this:
public class MyBean {
public void foo(Something something, Exchange exchange) {
if ("YES".equals(exchange.getProperty("awesome"))) {
// ...
}
}
}
Or via #Property like this:
public class MyBean {
public void foo(Something something, #Property String awesome) {
if ("YES".equals(awesome)) {
// ...
}
}
}
This presumes you are using later versions of camel.
Does this help?