I am new to C# and I am trying to build a handler function with Lambda to push messages to an SNS topic, this is what I have:
using MessagePublisher;
using Amazon.Lambda.Core;
using Amazon.SimpleNotificationService;
using Amazon.SimpleNotificationService.Model;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace SNSMessagePublisher
{
public class Function
{
public string PublishMessageHandler(NewMessage input, ILambdaContext context)
{
var client = new AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.EUWest2);
LambdaLogger.Log($"Calling function name: {context.FunctionName}\n");
var publishRequest = new PublishRequest(
"arn:aws:sns:eu-west-2:123...9:TopicABC",
input.body);
client.PublishAsync(publishRequest);
return $"Message Published: {input.body}";
}
}
}
namespace MessagePublisher {
public class NewMessage {
public string body { get; set; }
}
}
I then trigger a set payload of:
{
"body": "test body"
}
and in the CloudWatch logs, I get an output of:
Calling function name: messagePublisher
And the Lambda console returns:
"Message Published: test body"
However, the topic never actually receives a message.
client.PublishAsync(publishRequest); is asynchronous, will return a Task and as such, you need to wait on the task to finish executing using await.
Without calling await on the task, there is no guarantee that the client has published the message before the Lambda finishes executing.
The message is not being sent as the task for sending the message hasn't finished executing before the Lambda function has returned.
This should work:
public async Task<string> PublishMessageHandler(NewMessage input, ILambdaContext context)
{
var client = new AmazonSimpleNotificationServiceClient(Amazon.RegionEndpoint.EUWest2);
LambdaLogger.Log($"Calling function name: {context.FunctionName}\n");
var publishRequest = new PublishRequest(
"arn:aws:sns:eu-west-2:123...9:TopicABC",
input.body);
await client.PublishAsync(publishRequest);
return $"Message Published: {input.body}";
}
As you're new to C#, I would recommend reading Microsoft's Asynchronous programming with async and await article.
Related
I'd like to send a signal from my C# API code, probably using the SendSignal activity to a workflow that will receive that signal using the SignalReceived activity.
To send a signal to a workflow from your own code, such as a controller or any other class, you can use the ISignaler service.
Here's an example:
[ApiController]
[Route("myapi")]
public class MyApi: Controller
{
private readonly ISignaler _signaler;
public MyApiController(ISignaler signaler)
{
_signaler = signaler;
}
[HttpPost("send-signal")]
public async Task<IActionResult> SendSignal(CancellationToken cancellationToken)
{
var startedWorkflows = await _signaler.TriggerSignalAsync("MySignal", cancellationToken: cancellationToken);
return Ok(startedWorkflows);
}
}
A complete sample project can be found here.
I have 3 micro-services as following communicating via masstransit/rabbitmq
I'd like that the Api makes request to the TransactionService but got the response from the PspService.
namespace API
{
//I wish to have something like this
PaymentForm form = await requestClient.GetResponse<PaymentForm>(new CreatePayment())
}
namespace TransactionService
{
public class CreatePaymentConsumer : IConsumer<CreatePayment>
{
public async Task Consume(ConsumeContext<CreatePayment> context)
{
context.Send<BuildPaymentForm>()
}
}
}
namespace PspService
{
public class BuildPaymentFormConsumer : IConsumer<BuildPaymentForm>
{
public async Task Consume(ConsumeContext<BuildPaymentForm> context)
{
context.Response<PaymentForm>() //how to make sure that the response will be sent to the API, but not to the caller (TransactionService)?
}
}
}
Please point me to the right way to make this communication pattern or a similar example, or the right part in the documentation.
You can copy the RequestId and ResponseAddress from the CreatePayment message to the message produced by the first consumer using the CreateCopyContextPipe method. There is also a built-in way to copy all headers to the outgoing message (which I've used below).
namespace TransactionService
{
public class CreatePaymentConsumer : IConsumer<CreatePayment>
{
public async Task Consume(ConsumeContext<CreatePayment> context)
{
var pipe = context.CreateCopyContextPipe();
await _otherHost.Publish(new BuildPaymentForm(...), pipe);
}
}
}
namespace PspService
{
public class BuildPaymentFormConsumer : IConsumer<BuildPaymentForm>
{
public async Task Consume(ConsumeContext<BuildPaymentForm> context)
{
await context.RespondAsync(new PaymentForm(...));
}
}
}
The first consumer will publish the command to the second consumer, copying all headers to the outbound message. The second consumer will then respond to the request initiator.
I am using an Azure Function to get messages from a Rabbit MQ Broker to an Event Hub.
The function works perfect when I run it locally.
Here is the code of the function:
using System.Text;
using System.Dynamic;
using System.Threading.Tasks;
using CaseOnline.Azure.WebJobs.Extensions.Mqtt;
using CaseOnline.Azure.WebJobs.Extensions.Mqtt.Messaging;
using Microsoft.Azure.WebJobs;
using Newtonsoft.Json;
public static class Test
{
[FunctionName("EventHubOutput")]
public static async Task Run(
[MqttTrigger("topic/#")] IMqttMessage message,
[EventHub("eventhubname", Connection = "EventHubConnectionAppSetting")] IAsyncCollector<string> outputEvents,
ILogger log)
{
var body = message.GetMessage();
var bodyString = Encoding.UTF8.GetString(body);
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(bodyString);
obj.Topic = message.Topic;
await outputEvents.AddAsync(JsonConvert.SerializeObject(obj));
}
}
When deployed and run in the Azure portal, I get the following error messages:
Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: EventHubOutput
---> System.InvalidOperationException: Error while handling parameter outputEvents after function returned:
---> System.Net.Sockets.SocketException (0xFFFDFFFF): Name or service not known
at (...)
Any idea what the problem might be?
Thank you.
You are using the bindings incorrectly. Check out RabbitMQ bindings for Azure Functions overview.
The following example shows a C# function that reads and logs the RabbitMQ message as a RabbitMQ Event:
[FunctionName("RabbitMQTriggerCSharp")]
public static void RabbitMQTrigger_BasicDeliverEventArgs(
[RabbitMQTrigger("queue", ConnectionStringSetting = "rabbitMQConnectionAppSetting")] BasicDeliverEventArgs args,
ILogger logger
)
{
logger.LogInformation($"C# RabbitMQ queue trigger function processed message: {Encoding.UTF8.GetString(args.Body)}");
}
The following example shows how to read the message as a POCO:
namespace Company.Function
{
public class TestClass
{
public string x { get; set; }
}
public class RabbitMQTriggerCSharp{
[FunctionName("RabbitMQTriggerCSharp")]
public static void RabbitMQTrigger_BasicDeliverEventArgs(
[RabbitMQTrigger("queue", ConnectionStringSetting = "rabbitMQConnectionAppSetting")] TestClass pocObj,
ILogger logger
)
{
logger.LogInformation($"C# RabbitMQ queue trigger function processed message: {pocObj}");
}
}
}
I recommend you to check out this complete guide to setup Rabbit MQ Trigger in Azure Functions: RabbitMQ trigger for Azure Functions overview
im new to Akka.net, im running on linux and using .NET Core 3.1, i have written a very simple code but it does not work and i dont know why.
this my program.cs where i created the ActorSystem and simply called another actor
using Akka.Actor;
namespace PBFT
{
class Program
{
static void Main(string[] args)
{
var MyActorSystem = ActorSystem.Create("ActorSystem");
var Primary = MyActorSystem.ActorOf<PrimaryActor>();
Primary.Tell("Test");
}
}
}
and this is the first actor that is supposed to receive the message and simply outputs it to the console
using Akka.Actor;
using Akka;
using Akka.Event;
namespace PBFT
{
class PrimaryActor : ReceiveActor
{
private readonly ILoggingAdapter log = Context.GetLogger();
public PrimaryActor()
{
Receive<string>(message => System.Console.WriteLine(message));
}
}
}
the problem is there are no errors and the message does not get processsed by the Actor, am i missing something ?
Messages to and between actors in Akka.NET are passed asynchronously. What happens in your example, is that you're Telling a message to an actor and exiting the program immediately afterwards, before actor got a chance to process the message.
You can either suspend main thread (using eg. Console.ReadLine()) in your example, or - if you need to be sure that actor had processed the message before going forward - use combination of actor.Ask(message, cancellationToken) on the caller side (which will return Task, that completes once actor sends a response back) and Sender.Tell(response) inside your actor's receive method:
class PrimaryActor : ReceiveActor
{
private readonly ILoggingAdapter log = Context.GetLogger();
public PrimaryActor()
{
Receive<string>(message => {
System.Console.WriteLine(message);
Sender.Tell(new object()); // or any other response you want
});
}
}
class Program
{
static async Task Main(string[] args)
{
var MyActorSystem = ActorSystem.Create("ActorSystem");
var Primary = MyActorSystem.ActorOf<PrimaryActor>();
await Primary.Ask<object>("Test", default(CancellationToken));
}
}
I have an Azure Function HTTP triggered function which writes to Azure Table that may end in duplicated entries. I noticed that even if I try/catch'd the whole function, there will still be an Exception "leaked" to the function runner thus returning HTTP 500. Is there any way to catch this kind of exception?
Here's a minified version of the code:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage;
namespace FunctionTest
{
public class Entry
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
}
public static class Debug
{
[FunctionName("Debug")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequest req,
[Table("Debug")]
IAsyncCollector<Entry> tableBinding,
ILogger log)
{
try
{
await tableBinding.AddAsync(new Entry()
{
PartitionKey = "1111",
RowKey = "1111",
});
await tableBinding.FlushAsync();
}
catch (StorageException)
{
// we expect an Exception "The specified entity already exists"
return new OkObjectResult("This passes test");
}
return new OkObjectResult("This passes test too");
}
}
}
The code is written under Azure Function runtime 2.0 (the .NET Core one).
Trigger /api/debug twice or more and you will see:
HTTP 500
The catch{} code is entered, and still returns an HTTP 500(!)
In Application Insights, two table dependency call per request (shouldn't happen, the documentation says table do not have auto retry)
I guess, that using IAsyncCollector<> breaks things here. If you want to avoid such problems, try to exchange the following binding:
[Table("Debug")] IAsyncCollector<Entry> tableBinding
to:
[Table("Debug")] CloudTable tableBinding
Then, instead of using tableBinding.AddAsync() use the following snippet:
var op = TableOperation.Insert(new Entry());
await tableBinding.ExecuteAsync(op);
With that approach, you should be able to catch the exception, without leaking it to the Functions runtime.
Your try/catch block should look like following to catch all errors
try
{
}
catch (StorageException)
{
return new OkObjectResult("This passes test");
}
catch (Exception ex)
{
// return different error code
}