I have been stuck on a case for a while now, and i hope someone can help me out.
I am trying to receive data from an EventHub from Azure and to display these data (in real-time) on an ASP.NET page, using MVC.
I found a way to create an async method which would await to receive an event, and i refreshed this method and partial view with jQuery each second, but i found that this gave me too much instability over time and it would end in an endless loop of errors.
Here is my code:
public async Task<ActionResult> Index()
{
time = DateTime.Now;
list = new List<TestEntity>();
TestEntity t = new TestEntity();
list.Add(t);
await Initialize();
return View(list);
}
public async Task<ActionResult> RefreshLatest()
{
try
{
var message = await consumer.ReceiveAsync();
if (message != null)
{
TestEntity t = JsonConvert.DeserializeObject<TestEntity>(Encoding.UTF8.GetString(message.GetBytes()));
list.Add(t);
}
}
catch (Exception exception)
{
Console.WriteLine("exception on receive {0}", exception.Message);
}
return PartialView("Latest", list);
}
private static async Task Initialize()
{
EventHubClient eventHubClient = getEventHubClient(SharedAccessKeyName, SharedAccessKey, NamespaceURI, EventHubName, ConnectionString);
EventHubConsumerGroup consumerGroup = eventHubClient.GetDefaultConsumerGroup();
consumer = await consumerGroup.CreateReceiverAsync(partitionId, DateTime.Now, receiverEpoch); // All messages
}
public static EventHubClient getEventHubClient(string SharedAccessKeyName, string SharedAccessKey, string NamespaceURI, string EventHubName, string ConnectionString)
{
//Create EventHub
TokenProvider td = TokenProvider.CreateSharedAccessSignatureTokenProvider(SharedAccessKeyName, SharedAccessKey);
NamespaceManager manager = new NamespaceManager(NamespaceURI, td);
var description = manager.CreateEventHubIfNotExists(EventHubName);
//Create EventHubClient
EventHubClient client = EventHubClient.CreateFromConnectionString(ConnectionString + ";EntityPath=" + EventHubName);
return client;
}
And here is my Index View:
http://puu.sh/gDQUj/cb816b8870.png
I hope you can see what i'm trying to accomplish and how this fails.
Related
I have looked through samples in the github repo, but when i develop my process, i get "Connection ID required" when accessing the route that is mapped to a custom ConnectionHandler. My log message is never printed, nor do i land in the implementation with the debugger.
Startup:
builder.Services.AddConnections();
app.UseEndpoints(endpoints =>
{
endpoints.MapConnectionHandler<CustomDelegationHandler>("/proxy/{id}");
});
Implementation:
public class CustomDelegationHandler : ConnectionHandler
{
private readonly ILogger<CustomDelegationHandler> _logger;
public CustomDelegationHandler(ILogger<CustomDelegationHandler> logger)
{
_logger = logger;
}
public override async Task OnConnectedAsync(ConnectionContext connection)
{
_logger.LogWarning("Connection incoming");
while (true)
{
var result = await connection.Transport.Input.ReadAsync();
var buffer = result.Buffer;
try
{
if (!buffer.IsEmpty)
{
var stream = new MemoryStream();
var data = buffer.ToArray();
await stream.WriteAsync(data, 0, data.Length);
stream.Position = 0;
}
else if (result.IsCompleted)
{
break;
}
}
finally
{
connection.Transport.Input.AdvanceTo(buffer.End);
}
}
}
}
You need add the connection like below and it will map the custom ConnectionHandler:
public async Task<IActionResult> Index()
{
var url = "https://localhost:yourPortNumber/proxy/1";
var connection = new HttpConnection(new Uri(url));
await connection.StartAsync();
var bytes = Encoding.UTF8.GetBytes("aaaa");
async Task SendMessage()
{
await connection.Transport.Output.WriteAsync(bytes);
}
// Send the receive concurrently so that back pressure is released
// for server -> client sends
await SendMessage();
return View();
}
I am working with Azure Event Hub producer client and reading messages off of a kafka stream then pass it along to deserialize/map, then pass to Event Hub. I have the consume loop which is creating a task for each consume and then two methods to do processing(this seems to have greatly improved the speed from the kafka lag perspective. However, Event hub makes you create an event batch which I don't necessarily want to use. I just want to send the data one message at a time for now. In order to create a new batch I have to call Dispose(). I am running into an issue where there's another call to the function by the the time I call Dispose() and I get an error saying the object is being used by event hub.
I've also tried using the overload for eventHubProducerClient.SendAsync that allows you to pass in a IEnumerable but i'm running into the same issue with that.
So I believe this to be a synchronization issue, or maybe I need to do a lock somewhere?
Any help would be appreciated.
public void Execute()
{
using (_consumer)
{
try
{
_consumer.Subscribe(_streamConsumerSettings.Topic);
while (true)
{
var result = _consumer.Consume(1000);
if (result == null)
{
continue;
}
var process = Task.Factory.StartNew(() => ProcessMessage(result?.Message?.Value));
var send = process.ContinueWith(t => SendMessage(process.Result));
}
}
catch (ConsumeException e)
{
_logger.LogError(e, e.StackTrace ?? e.Message);
_cancelConsume = true;
_consumer.Close();
RestartConsumer();
}
}
}
public static EquipmentJson ProcessMessage(byte[] result)
{
var json = _messageProcessor.DeserializeAndMap(result);
return json;
}
public static void SendMessage(EquipmentJson message)
{
try
{
_eventHubClient.AddToBatch(message);
}
catch (Exception e)
{
_logger.LogError(e, e.StackTrace ?? e.Message);
}
}
public async Task AddToBatch(EquipmentJson message)
{
if
(!string.IsNullOrEmpty(message.EquipmentLocation))
{
try
{
var batch = await _equipmentLocClient.CreateBatchAsync();
batch.TryAdd(new EventData(Encoding.UTF8.GetBytes(message.EquipmentLocation)));
await _eventHubProducerClient.SendAsync(batch);
batch.Dispose();
_logger.LogInformation($"Data sent {DateTimeOffset.UtcNow}");
}
catch (Exception e)
{
_logger.LogError(e, e.StackTrace ?? e.Message);
}
}
}
public class EventHubClient : IEventHubClient
{
private readonly ILoggerAdapter<EventHubClient> _logger;
private readonly EventHubClientSettings _eventHubClientSettings;
private IMapper _mapper;
private static EventHubProducerClient _equipmentLocClient;
public EventHubClient(ILoggerAdapter<EventHubClient> logger, EventHubClientSettings eventHubClientSettings, IMapper mapper)
{
_logger = logger;
_eventHubClientSettings = eventHubClientSettings;
_mapper = mapper;
_equipmentLocClient = new EventHubProducerClient(_eventHubClientSettings.ConnectionString, _eventHubClientSettings.EquipmentLocation);
}
}
}
Based on my speculation in comments, I'm curious if refactoring to use async/await rather than the explicit continuation in the main loop may help. Perhaps something similar to the following LinqPad snippet:
async Task Main()
{
while (true)
{
var message = await Task.Factory.StartNew(() => GetText());
var events = new[] { new EventData(Encoding.UTF8.GetBytes(message)) };
await Send(events).ConfigureAwait(false);
}
}
public EventHubProducerClient client = new EventHubProducerClient("<< CONNECTION STRING >>");
public async Task Send(EventData[] events)
{
try
{
await client.SendAsync(events).ConfigureAwait(false);
"Sent".Dump();
}
catch (Exception ex)
{
ex.Dump();
}
}
public string GetText()
{
Thread.Sleep(250);
return "Test";
}
If you're set on keeping the continuation, I wonder if a slight structural refactoring in the continuation may help, both to push up creation of the events and to honor the await statements. Perhaps something similar to the following LinqPad snippet:
async Task Main()
{
while(true)
{
var t = Task.Factory.StartNew(() => GetText());
var _ = t.ContinueWith(async q =>
{
var events = new[] { new EventData(Encoding.UTF8.GetBytes(t.Result)) };
await Send(events).ConfigureAwait(false);
});
await Task.Yield();
}
}
public EventHubProducerClient client = new EventHubProducerClient("<< CONNECTION STRING >>");
public async Task Send(EventData[] events)
{
try
{
await client.SendAsync(events).ConfigureAwait(false);
"Sent".Dump();
}
catch (Exception ex)
{
ex.Dump();
}
}
public string GetText()
{
Thread.Sleep(250);
return "Test";
}
I have the following SignalR Hub with in-memory connection management.
[Authorize]
public class ChatHub : Hub
{
private static readonly ConnectionMapping<string> _connections =
new ConnectionMapping<string>();
private readonly IMessageRepository _messagesRepository;
public ChatHub(IMessageRepository messageRepository)
{
_messagesRepository = messagesRepository;
}
public override async Task OnConnectedAsync()
{
var name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
//await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception exception)
{
var name = Context.User.Identity.Name;
_connections.Remove(name, Context.ConnectionId);
//await Groups.RemoveFromGroupAsync(Context.ConnectionId, "SignalR Users");
await base.OnDisconnectedAsync(exception);
}
public async void SendMessage(ChatMessage chatMessage)
{
var name = Context.User.Identity.Name;
var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);
var availableConnections = _connections.GetConnections(name);
if (availableConnections.Any())
{
foreach (var connectionId in availableConnections)
{
Clients.Client(connectionId).SendAsync("ReceiveMessage", chatHistoryMessage);
}
}
else
{
}
}
}
However, when executing the code the following line
Clients.Client(connectionId).SendAsync("ReceiveMessage", 1);
raises an object disposed error on Clients.
This issue started happening when I added the repository line:
var chatHistoryMessage = await _messagesRepository.SaveMessageAsync(chatMessage);
SaveMessageAsync method:
public async Task<ChatHistory> SaveMessageAsync (ChatMessage chatMessage)
{
using (var conn = new SqlConnection(ConnProvider.ConnectionString))
{
await conn.OpenAsync();
return (await conn.QueryAsync<ChatHistory>("[mob].[spSaveChatMessage]",
new
{
..
},
commandType: CommandType.StoredProcedure)).FirstOrDefault();
}
}
Why would my Clients object be disposed? If I wait with the debugger that issue never happens.
It looks like the SendMessage method should be an async Task rather than an async void.
The issue could be caused by the way the SignalR framework runs async voids.
See this article for a good overview.
Async methods returning void don’t provide an easy way to notify the
calling code that they’ve completed.
I have seen some of the existing questions regarding async waiting for completion , However for me none of the solution work.
I am using a C# wrapper for connecting to sales force https://github.com/developerforce/Force.com-Toolkit-for-NET/
In the below method i want to wait for the method UsernamePasswordAsync to complete execution so that i can get the values from the auth object.
public async Task<Token> GetTokenForSalesForce()
{
Token token = null;
try
{
var auth = new AuthenticationClient();
await auth.UsernamePasswordAsync(configuration.Value.ClientId, configuration.Value.ClientSecert,
configuration.Value.SFUsername, configuration.Value.SFPassword,
configuration.Value.SFBaseUrl);
if (!string.IsNullOrEmpty(auth.AccessToken) && !string.IsNullOrEmpty(auth.InstanceUrl))
{
token = new Token
{
BearerToken = auth.AccessToken,
InstanceURL = auth.InstanceUrl,
ApiVersion = auth.ApiVersion
};
}
}
catch (Exception ex)
{
throw ex;
}
return token;
}
public async Task<List<SFDashboardResponse>> GetOrderCountFromSalesForce(Token token)
{
List<SFDashboardResponse> sFDashboardResponses = new List<SFDashboardResponse>();
try
{
var client = new ForceClient(token.InstanceURL, token.BearerToken, token.ApiVersion);
var response = await client.QueryAsync<SFDashboardResponse>("SELECT something ");
var records = response.Records;
}
catch(Exception e)
{
}
return sFDashboardResponses;
}
The signature in the library is
public async Task WebServerAsync(string clientId, string clientSecret, string redirectUri, string code, string tokenRequestEndpointUrl)
{
}
The problem is while the method wait for await to be first execute another thread executes the other part of the orignal caller.
I call it from here
public IActionResult post()
{
var authtoken = _salesForceService.GetTokenForSalesForce();
var response = _salesForceService.GetOrderCountFromSalesForce(authtoken.Result);
DashboardModel dashboardModel = null;
if (authtoken.Status == TaskStatus.RanToCompletion)
{
fill the object
}
return Ok(dashboardModel);
}
You can wrap the IActionResult with a Task and await on the tasks below.
public async Task<IActionResult> post()
{
var authtoken = await _salesForceService.GetTokenForSalesForce();
var response = await _salesForceService.GetOrderCountFromSalesForce(authtoken);
DashboardModel dashboardModel = //fill the object
return Ok(dashboardModel);
}
At least this is what you are asking for as far as I understand, if its another problem let me know.
EDIT 1:
This is just my suggestion/opinion.
Personally I dont really like having the code wrapped in try-catch everywhere, this way the code can be hard to read and maintain. You really should consider centralizing exception handling in one place, you could have a base controller or just a middleware like this one:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context, ILogger logger)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, logger);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
{
logger.Log(exception);
//do something
return context.Response.WriteAsync(... something ...); //Maybe some JSON message or something
}
}
The you just register it as a middleware in the Configure method like below:
app.UseMiddleware<ErrorHandlingMiddleware>();
I'm working on a little console application that selects messages from a database queue
and forwards the messages to a rest api (ASP.NET Web Api).
In general the application does the following steps:
Get the number of pending messages
Load the last pending messages
Post the message to the rest api
Remove the message from the database
To make the program more flexible and to have the ability to process every single message
in a separate database transcation the steps 2 - 3 will be executed as tasks.
This means if there are four messages in the database, we'll have four tasks that
should run nearly parallel and process the messages.
This is what the code looks like:
Database message
public class DatabaseMessage
{
public string Message { get; set; }
}
UnitOfWork (Interface IUnitOfWork)
public class UnitOfWork
{
// ... extermely simplified
public int GetNumberOfPendingMessages() { ... }
public DatabaseMessage GetNextPendingMessage() { ... }
public void DeleteMessage(DatabaseMessage message) { ... }
}
HttpService (Interface IHttpService)
public class HttpService
{
private readonly HttpClient _httpClient;
public HttpService()
{
_httpClient = new HttpClient();
/* Some initalization stuff for the HttpClient object */
}
public async Task<HttpResponse> PostMessage(DatabaseMessage message)
{
var content = /* Create content object */
return await _httpClient.PostAsync(..., content);
}
}
MessageProcessingService (Interface IMessageProcessingService)
public class MessageProcessingService
{
private readonly IHttpService _httpService;
private readonly Semaphore _databaseProcessingSemaphore;
public MessageProcessingService(IHttpService httpService)
{
_httpService = httpService;
}
public async Task ProcessDatabaseMessages()
{
var unitOfWork = new UnitOfWork();
var numberOfPendingMessages = unitOfWork.GetNumberOfPendingMessages();
var messageProcessingTasks = new List<Task>();
for(int t = 0; t < numberOfPendingMessages; t++)
{
messageProcessingTasks.Add(new Task(() => {
ProcessMessageAsTask();
}));
}
var continuationHandler = Task.WhenAll(messageProcessingTasks);
messageProcessingTasks.ForEach(e => e.Start());
await continuationHandler;
}
private void ProcessMessageAsTask()
{
// Single unit of work for each tasks
var unitOfWork = new UnitOfWork();
try{
// Starting a database transaction
unitOfWork.StartTransaction();
_databaseProcessingSemaphore.OnWait();
var message = unitOfWork.GetNextPendingMessage();
_databaseProcessingSemaphore.Release();
if(message != null)
{
var response = _httpService.PostMessage(message).Result;
if(response == HttpStatus.OK)
{
unitOfWork.DeleteMessage(message);
unitOfWork.Commit();
}
else
{
unitOfWork.Rollback();
}
}
else
{
unitOfWork.Commit();
}
}
catch(Exception ex)
{
unitOfWork.Rollback();
// Further error handling...
}
}
}
For better understanding, the HttpClient object is created and managed by Unity and is injected
into the MessageProcessingService object. The HttpClient is held as singleton in the container.
I'm facing now the problem that the call of the method _httpService.PostMessage(). For example, if there are five
messages in the message queue, the call fails five times with an exception that tells me that an task has been canceled.
My question is now what is the problem with PostAsync call of the .NET HttpClient? Is the issue caused by the .Result option or would
it be better to create a new instance of the HttpClient for each message processing task?
Or is there a general problem with the architecture with tasks and the processing of rest api calls?
Update 2018-04-04 - 08:09
I've now made the method ProcessMessageAsTask async and I'm awaiting now the call of the HttpService.
But now I don't get any exception at all. In the ressource monitor and while debugging I can see that all tasks reach the call of the HttpClient (return await _httpClient.PostAsync(..., content);)
But there is no exception nor will the messages be posted. But I don't get any exceptions. The program will be closed immediately after the calls of the HttpClient. All futher statements were not processed.
Changes:
public async Task ProcessDatabaseMessages()
{
var unitOfWork = new UnitOfWork();
var numberOfPendingMessages = unitOfWork.GetNumberOfPendingMessages();
var messageProcessingTasks = new List<Task>();
for(int t = 0; t < numberOfPendingMessages; t++)
{
messageProcessingTasks.Add(new Task(async () => {
await ProcessMessageAsTask();
}));
}
var continuationHandler = Task.WhenAll(messageProcessingTasks);
messageProcessingTasks.ForEach(e => e.Start());
await continuationHandler;
}
private async Task ProcessMessageAsTask()
{
// Single unit of work for each tasks
var unitOfWork = new UnitOfWork();
try{
// Starting a database transaction
unitOfWork.StartTransaction();
_databaseProcessingSemaphore.OnWait();
var message = unitOfWork.GetNextPendingMessage();
_databaseProcessingSemaphore.Release();
if(message != null)
{
var response = await _httpService.PostMessage(message);
if(response == HttpStatus.OK)
{
unitOfWork.DeleteMessage(message);
unitOfWork.Commit();
}
else
{
unitOfWork.Rollback();
}
}
else
{
unitOfWork.Commit();
}
}
catch(Exception ex)
{
unitOfWork.Rollback();
// Further error handling...
}
}