Exception in durable function - azure-durable-functions

I have the following durable function code:
[FunctionName("OrchestratorFunction")]
public async Task RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var jobs = await context.CallActivityAsync<List<Job>>("JobsReaderFunction"), null);
if (jobs != null && jobs .Count > 0)
{
var processingTasks = new List<Task>();
foreach (var job in jobs)
{
Task processTask = context.CallSubOrchestratorAsync("SubOrchestratorFunction"), job);
processingTasks.Add(processTask);
}
await Task.WhenAll(processingTasks);
}
}
[FunctionName("SubOrchestratorFunction")]
public async Task RunSubOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var job = context.GetInput<Job>();
var group = await context.CallActivityAsync<Group>("GroupReaderFunction"), job);
await context.CallActivityAsync("EmailSenderFunction", group);
}
[FunctionName("GroupReaderFunction")]
public async Task<JobGroup> GetGroup([ActivityTrigger] Job job)
{
var group = new JobGroup();
if (job != null)
{
var group = await _jobTopicService.GetGroupAsync(job.TargetOfficeGroupId);
group = new JobGroup
{
Job = job,
Name = name
};
}
return group;
}
While running this, I get the following exception:
Exception while executing function: GroupReaderFunction The request message was already sent. Cannot send the same request message multiple times.
What could be the issue? Any suggestions would be helpful.

Related

How to get messages from Azure Service Bus (Queue) in View.cshtml in ASP.NET MVC web app

I have a console application with which I can get messages from Azure Service Bus (Queue).
using System;
using System.Text;
using System.Text.Json;
using Microsoft.Azure.ServiceBus;
using SampleShared.Models;
namespace SampleAppReceiver
{
class Program
{
const string connString = "<my_connection_string>";
static IQueueClient qClient;
static async Task Main(string[] args)
{
qClient = new QueueClient(connString, "<my_queue_name>");
var msgOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
{
// How many messages we can process at time
MaxConcurrentCalls = 1,
// need to wait until a message is fully processed
AutoComplete = false,
};
qClient.RegisterMessageHandler(ProcessMessageAsync, msgOptions);
Console.ReadLine();
await qClient.CloseAsync();
}
private static async Task ProcessMessageAsync(Message msg, CancellationToken token)
{
// Deserialise the msg body
var jsonBody = Encoding.UTF8.GetString(msg.Body);
var personObj = JsonSerializer.Deserialize<Person>(jsonBody);
Console.WriteLine($"Login: {personObj.Login}");
Console.WriteLine($"Message: {personObj.Message}");
// Updating the queue that the message has been processed sucsessfully
await qClient.CompleteAsync(msg.SystemProperties.LockToken);
}
private static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs args)
{
Console.WriteLine($"Something went wrong, {args.Exception}");
return Task.CompletedTask;
}
}
}
How can I correctly add all the received messages to View.cshtml from controller?
Now I have a service (C# interface) with which I can send messages from View.cshtml to Azure Service Bus (queue):
// ...
public interface IAzureBusService
{
Task SendMessageAsync(Person personMessage, string queueName);
}
// ...
Controller method:
[HttpPost]
public async Task<IActionResult> Index(Person person)
{
await _busService.SendMessageAsync(person, "personqueue");
return RedirectToAction("Index");
}
Create a service Bus in Azure portal.
Create a Queue as per the below screenshot.
I followed the below steps in displaying the queue messages in a view.
You can use the console application reference in your MVC project to display queue messages in a View by calling the method of fetching the messages from queue.
You need to use the below code in the controller class.
public ActionResult Index()
{
List<QueueMsgs> queMsglist = new List<QueueMsgs>();
QueueMsgs msgs = new QueueMsgs();
queMsglist = GetMessagesFromQueue();
return View(queMsglist);
}
public void GetMessagesFromQueue()
{
ServiceBusReceiver receiver = new ServiceBusReceiver();
receiver.Listener();
}
public void Listener()
{
ServiceBusConnectionStringBuilder conStr;
QueueClient client;
try
{
conStr = new ServiceBusConnectionStringBuilder(QueueAccessKey);
client = new QueueClient(conStr, ReceiveMode.ReceiveAndDelete, RetryPolicy.Default);
var messageHandler = new MessageHandlerOptions(ListenerExceptionHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
client.RegisterMessageHandler(ReceiveMessageFromQ, messageHandler);
}
catch (Exception exe)
{
Console.WriteLine("{0}", exe.Message);
Console.WriteLine("Please restart application ");
}
public async Task ReceiveMessageFromQ(Message message, CancellationToken token)
{
string result = Encoding.UTF8.GetString(message.Body);
Console.WriteLine("Message received from Queue = {0}", result);
await Task.CompletedTask;
}
public Task ListenerExceptionHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
Console.WriteLine("{0}", exceptionReceivedEventArgs.Exception);
return Task.CompletedTask;
}
#model IEnumerable<MVC.Models.QueueMsgs>
#{
ViewBag.Title = "Queue Messages";
}
#foreach (var item in Model)
{
<div>
#item.message
<hr />
</div>
}
Displaying Queue Messages in View.

Passing parameters to a SignalR Hub (ASP NET Core 6)

how can i pass parameters to a asynchronous task of a SignalR Hub?
The paramaeters id, dis and dg have to be passes to the asynchronous task SendResults().
My hub:
public class ResultHub : Hub
{
ResultRepository ResultRepository;
public ResultHub(IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("DefaultConnection");
ResultRepository = new ResultRepository(connectionString);
}
public async Task SendResults()
{
int id = 2977;
int dis = 3;
int dg = 1;
var Results = ResultRepository.GetResults(id, dis, dg);
await Clients.All.SendAsync("ReceivedResults", Results);
}
}
The asynchronous task SendResults gets the results with ResultRepository.GetResults.
SendResults is called in the Javascript within the chtml file:
function InvokeResults() {
connection.invoke("SendResults").catch(function (err) {
return console.error(err.toString());
});
}
and in the method TableDependency_OnChanged of the class SubscribeResultTableDependency
public class SubscribeResultTableDependency : ISubscribeTableDependency
{
SqlTableDependency<Result> tableDependency;
ResultHub ResultHub;
public SubscribeResultTableDependency(ResultHub resultHub)
{
this.resultHub = resultHub;
}
public void SubscribeTableDependency(string connectionString)
{
tableDependency = new SqlTableDependency<Result>(connectionString);
tableDependency.OnChanged += TableDependency_OnChanged;
tableDependency.OnError += TableDependency_OnError;
tableDependency.Start();
}
private void TableDependency_OnChanged(object sender, TableDependency.SqlClient.Base.EventArgs.RecordChangedEventArgs<Result> e)
{
if (e.ChangeType != TableDependency.SqlClient.Base.Enums.ChangeType.None)
{
resultHub.SendResults();
}
}
private void TableDependency_OnError(object sender, TableDependency.SqlClient.Base.EventArgs.ErrorEventArgs e)
{
Console.WriteLine($"{nameof(Result)} SqlTableDependency error: {e.Error.Message}");
}
}
Passing of parameters in the connection.invoke of the Javascript works, but how can this be done in both calls?
(Microsoft.NETCore.App\6.0.13)
According to your description, if you want to how to pass the parameter from the js to the Hub method, I suggest you could refer to below example:
1.Modify the hub method to add parameter, like below:
public class ChatHub : Hub
{
public async Task SendResults(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
2.Modify the js to add the parameter:
connection.invoke("SendResults", "parameter1", "parameter2").catch(function (err) {
return console.error(err.toString());
});

Problem in Createing WebSocketProxy For Immplement embeded NOVNC in Asp.net Core 6

I want to embed novnc console in an iframe into a .net web application. My openstack is configured to recieve http requests and my web application sends https requests.
When I use iframe to show my novnc console in asp.net web application, the console sends WSS request to my server but the openstack horizon accepts WS requests, because of that I create a middleware to convert requests from https to http & WSS to WS but it didn't work, I don't know what is the problem, my console is shown but the connection is closed immediately.
my reverse proxy for handle http request is:
namespace ReverseProxyApplication
{
public class ReverseProxyMiddleware
{
private static readonly HttpClient _httpClient = new HttpClient();
private readonly RequestDelegate _nextMiddleware;
public ReverseProxyMiddleware(RequestDelegate nextMiddleware)
{
_nextMiddleware = nextMiddleware;
}
public async Task Invoke(HttpContext context)
{
var targetUri = BuildTargetUri(context.Request);
if (targetUri != null)
{
var targetRequestMessage = CreateTargetMessage(context, targetUri);
using (var responseMessage = await _httpClient.SendAsync(targetRequestMessage, HttpCompletionOption.ResponseHeadersRead, context.RequestAborted))
{
context.Response.StatusCode = (int)responseMessage.StatusCode;
CopyFromTargetResponseHeaders(context, responseMessage);
await responseMessage.Content.CopyToAsync(context.Response.Body);
}
return;
}
await _nextMiddleware(context);
}
private HttpRequestMessage CreateTargetMessage(HttpContext context, Uri targetUri)
{
var requestMessage = new HttpRequestMessage();
CopyFromOriginalRequestContentAndHeaders(context, requestMessage);
requestMessage.RequestUri = targetUri;
requestMessage.Headers.Host = targetUri.Host;
requestMessage.Method = GetMethod(context.Request.Method);
return requestMessage;
}
private void CopyFromOriginalRequestContentAndHeaders(HttpContext context, HttpRequestMessage requestMessage)
{
var requestMethod = context.Request.Method;
if (!HttpMethods.IsGet(requestMethod) &&
!HttpMethods.IsHead(requestMethod) &&
!HttpMethods.IsDelete(requestMethod) &&
!HttpMethods.IsTrace(requestMethod))
{
var streamContent = new StreamContent(context.Request.Body);
requestMessage.Content = streamContent;
}
foreach (var header in context.Request.Headers)
{
requestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value.ToArray());
}
}
private void CopyFromTargetResponseHeaders(HttpContext context, HttpResponseMessage responseMessage)
{
foreach (var header in responseMessage.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
foreach (var header in responseMessage.Content.Headers)
{
context.Response.Headers[header.Key] = header.Value.ToArray();
}
context.Response.Headers.Remove("transfer-encoding");
}
private static HttpMethod GetMethod(string method)
{
if (HttpMethods.IsDelete(method)) return HttpMethod.Delete;
if (HttpMethods.IsGet(method)) return HttpMethod.Get;
if (HttpMethods.IsHead(method)) return HttpMethod.Head;
if (HttpMethods.IsOptions(method)) return HttpMethod.Options;
if (HttpMethods.IsPost(method)) return HttpMethod.Post;
if (HttpMethods.IsPut(method)) return HttpMethod.Put;
if (HttpMethods.IsTrace(method)) return HttpMethod.Trace;
return new HttpMethod(method);
}
private Uri BuildTargetUri(HttpRequest request)
{
Uri targetUri = null;
if (request.Path.StartsWithSegments("/horizon", out var remainingPath))
{
targetUri = new Uri("http://192.168.66.11:6080" + remainingPath);
}
return targetUri;
}
}
}
and my web socket middle ware
public class WebSocketMiddleware
{
private static ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket>();
private readonly RequestDelegate _next;
public WebSocketMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (!context.WebSockets.IsWebSocketRequest)
{
await _next.Invoke(context);
return;
}
var buffer = new ArraySegment<byte>(new byte[8192]);
Uri targetUri = new Uri("ws://192.168.66.11:6080/" + context.Request.QueryString.Value);
ClientWebSocket _websocketClient = new ClientWebSocket();
await _websocketClient.ConnectAsync(targetUri, CancellationToken.None);
// if (_websocketClient.State == WebSocketState.Open)
// {
// // await _websocketClient.SendAsync(buffer, WebSocketMessageType.Binary, true, CancellationToken.None);
// var result = await _websocketClient.ReceiveAsync(buffer, CancellationToken.None);
// if (result.MessageType == WebSocketMessageType.Close)
// {
// await _websocketClient.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
// }
// }
CancellationToken ct = context.RequestAborted;
WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync();
var socketId = Guid.NewGuid().ToString();
_sockets.TryAdd(socketId, currentSocket);
foreach (var socket in _sockets)
{
if (socket.Value.State != WebSocketState.Open)
{
continue;
}
await SendStringAsync(socket.Value, _websocketClient, buffer, ct);
}
while (true)
{
if (ct.IsCancellationRequested)
{
break;
}
var response = await ReceiveStringAsync(currentSocket, _websocketClient, context, ct);
if (response != null)
{
if (currentSocket.State != WebSocketState.Open)
{
break;
}
//continue;
foreach (var socket in _sockets)
{
if (socket.Value.State != WebSocketState.Open)
{
continue;
}
await SendStringAsync(socket.Value, _websocketClient, response, ct);
}
}
}
WebSocket dummy;
_sockets.TryRemove(socketId, out dummy);
await currentSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", ct);
currentSocket.Dispose();
}
private static Task SendStringAsync(WebSocket socket, ClientWebSocket _websocketClient, ArraySegment<byte> data, CancellationToken ct = default(CancellationToken))
{
//var buffer = Encoding.UTF8.GetBytes(data);
var segment = new ArraySegment<byte>(new byte[8192]);
if (_websocketClient.State == WebSocketState.Open)
{
_websocketClient.ReceiveAsync(segment, CancellationToken.None);
//_websocketClient.SendAsync(segment, WebSocketMessageType.Binary, true, CancellationToken.None);
}
return socket.SendAsync(segment, WebSocketMessageType.Binary, true, ct);
}
private static async Task<ArraySegment<byte>> ReceiveStringAsync(WebSocket socket, ClientWebSocket _websocketClient, HttpContext context, CancellationToken ct = default(CancellationToken))
{
var buffer = new ArraySegment<byte>(new byte[8192]);
WebSocketReceiveResult result;
do
{
ct.ThrowIfCancellationRequested();
result = await socket.ReceiveAsync(buffer, ct);
} while (!result.EndOfMessage);
// Uri targetUri = new Uri("ws://192.168.66.11:6080/" + context.Request.QueryString.Value);
//ClientWebSocket _websocketClient = new ClientWebSocket();
//await _websocketClient.ConnectAsync(targetUri, CancellationToken.None);
if (_websocketClient.State == WebSocketState.Open)
{
bool hasAllZeroes = buffer.All(singleByte => singleByte == 0);
if (!hasAllZeroes)
await _websocketClient.SendAsync(buffer, WebSocketMessageType.Binary, true, CancellationToken.None);
//var myresult = await _websocketClient.ReceiveAsync(buffer, CancellationToken.None);
}
return buffer == new ArraySegment<byte>(new byte[8192]) ? null : buffer;
}
}
when i load the page this is my wss request
novnc console in i frame
js errors
wss request to middle ware
.
Use the IHttpForwarder from YARP https://microsoft.github.io/reverse-proxy/articles/direct-forwarding.html. It handles websocket, grpc etc etc.

MVC SignalR not firing from Controller Post Method

When Saving schedule to calendar it must auto update the activity logs on my notification bar in my Home Controller. It saves the data but only show when notification bar is refreshed. It seems that Hub is not starting when saved.
CalendarController.cs
[HttpPost]
public JsonResult SaveSchedule(Schedule s)
{
var userid = User.Identity.GetUserId();
var profile = _context.Profiles.Single(p => p.Id == userid);
var status = false;
if (s.Schedule_ID > 0)
{
//Update
var v = _context.Schedules.Where(a => a.Schedule_ID == s.Schedule_ID).FirstOrDefault();
if (v != null)
{
v.Shift = s.Shift;
}
}
var activitylog = new ActivityLog
{
UserId = userid,
LogDate = DateTime.Now,
Activity = ActivityHelper.GetActivityLog(4, profile.FirstName)
};
// save to data and must be shown on notification bar
_context.ActivityLogs.Add(activitylog);
_context.SaveChanges();
ActivityHub.StartLogging();
status = true;
return new JsonResult { Data = new { status = status } };
}
HomeController.cs
public JsonResult GetLogs()
{
return Json(ActivityHelper.GetActivityLogs(), JsonRequestBehavior.AllowGet);
}
ActivityHub.cs
public class ActivityHub : Hub
{
public static void StartLogging()
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
//calls the signalR client part to execute javascript method
context.Clients.All.displayLog();
}
}
My CSHTML
<script>
$(function () {
var activityFromHub = $.connection.activityHub;
$.connection.hub.start().done(function () {
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
function FetchLogs() {
$.ajax({
type: 'GET',
url: '/Home/GetLogs',
datatype: 'json',
success: function (data) {
$("#logs tr").remove();
data = $.parseJSON(data);
if (data.length > 0) {
.... do some append here
}
},
error: function (error) {
alert("error");
}
});
}
});
</script>
ActivityHelper.cs
static readonly string connString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
public static class ActivityHelper
{
public static string GetActivityLogs()
{
string sqlCommand = #"my select query here";
try
{
var messages = new List<ActivityLog>();
using(var connection = new SqlConnection(connString))
{
connection.Open();
using (SqlConnection con = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(sqlCommand, con);
if(con.State != System.Data.ConnectionState.Open)
{
con.Open();
}
cmd.Notification = null;
SqlDependency dependency = new SqlDependency(cmd);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
messages.Add(item: new ActivityLog
{
Activity = reader["Activity"] != DBNull.Value ? (string)reader["Activity"] : "",
LogDate = (DateTime)reader["LogDate"]
});
}
}
}
var jsonSerialiser = new JavaScriptSerializer();
var json = jsonSerialiser.Serialize(messages);
return json;
}
catch(Exception ex)
{
throw;
}
}
public static void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
if (e.Type == SqlNotificationType.Change)
{
SqlDependency dependency = sender as SqlDependency;
dependency.OnChange -= dependency_OnChange;
var activityHub = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
GetActivityLogs();
}
}
}
FIRST METHOD
First Solution change your javascript code like this. If this not works move to the second method:
$(function () {
var activityFromHub = $.connection.ActivityHub;
$.connection.hub.start().done(function () {
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
});
SECOND METHOD:
Each client connecting to a hub passes a unique connection id. You can retrieve this value in the Context.ConnectionId property of the hub context. And i found there is nothing happening like this. You may try this solution.
I think the simplest solution for your question is to use groups.
http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups
Your hub class would contain methods to join a group:
public Task JoinGroup(string groupName)
{
return Groups.Add(Context.ConnectionId, groupName);
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
and your hub will be look like this:
public static void StartLogging(string groupName)
{
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ActivityHub>();
context.Clients.Group(groupName).displayLog();
//calls the signalR client part to execute javascript method
//context.Clients.All.displayLog();
}
And change your javascript as like this:
$(function () {
var activityFromHub = $.connection.ActivityHub;
$.connection.hub.start().done(function () {
activityFromHub.server.joinGroup("Group1");
activityFromHub.server.StartLogging("Group1");
FetchLogs();
});
activityFromHub.client.displayLog = function () {
console.log('Hub Started');
FetchLogs();
}
});
I hope this will resolve your issue. If you are still facing issue. Please leave comments. Thank you.

An asynchronous module or handler completed while an asynchronous operation was still pending

Description :
I'm sending an email from MVC application, mail was sent successfuly when I send it to a single person but when I send mail to multiple people then getting an exception i.e. Operation has timed out.
So, to avoid this problem I'm trying use smtpclient.SendAsync(message,object) method but I'm getting an exception i.e. An asynchronous module or handler completed while an asynchronous operation was still pending.
How to resolve this problem ?
Code:
public async Task<string> SendEmail(List<string> ToEmailAddresses,string body, string emailSubject)
{
var smtp = new SmtpClient
{
DeliveryMethod = SmtpDeliveryMethod.Network,
Host = "xyz-host-name",
Port = 25,
EnableSsl = false,
Timeout = 600000
};
var fromAddress = new MailAddress(ConfigurationManager.AppSettings["MailUserName"], "Rewards and Recognition Team");
using (var message = new MailMessage() { Subject = emailSubject, Body = body })
{
message.From = fromAddress;
foreach (string email in ToEmailAddresses)
{
message.To.Add(email);
}
message.IsBodyHtml = true;
try
{
_logger.Log("EmailService-SendEmail-try");
object userToken = message;
smtp.SendAsync(message,userToken);
return "Success";
}
catch (Exception ex)
{
_logger.Log("EmailService-SendEmail-" + ex.Message);
return "Error";
}
}
}
you have to use the await keyword in actionresult (controller side)
like
public async task<ActionResult> SendMail(object obj)
{
var result = await SendEmail(ToEmailAddresses,body,emailSubject)
return result
}

Resources