I want to Subscribe for Azure Event Grid in C# Console Application and that application is not hosted on Azure(will be on Windows server).
Other C# webapi project will fire Azure Event that will be hosted on Azure but how to Subscribe(listen) for Event on VM that is not hosted on Azure?
Which Endpoint detail should I select for above scenario?
One idea would be to push the event to the event hub by setting an endpoint to the dedicated EH from the event grid(like from your picture). Then you can implement the listener on your VM that is reading all the events from the dedicated endpoint.
Below is the small code sample of how your script should look like, and essentially it is just a small console application that reads the events from the event hub and writes to the console:
public class MyEventProcessor : IEventProcessor
{
private Stopwatch checkpointStopWatch;
public Task ProcessErrorAsync(PartitionContext context, Exception error)
{
Console.WriteLine(error.ToString());
return Task.FromResult(true);
}
async Task IEventProcessor.CloseAsync(PartitionContext context, CloseReason reason)
{
if (reason == CloseReason.Shutdown)
{
await context.CheckpointAsync();
}
}
Task IEventProcessor.OpenAsync(PartitionContext context)
{
var eventHubPartitionId = context.PartitionId;
Console.WriteLine($"Registered reading from the partition: {eventHubPartitionId} ");
this.checkpointStopWatch = new Stopwatch();
this.checkpointStopWatch.Start();
return Task.FromResult<object>(null);
}
//Data comes in here
async Task IEventProcessor.ProcessEventsAsync(PartitionContext context, IEnumerable<EventData> messages)
{
foreach (var eventData in messages)
{
var data = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count);
Console.WriteLine($"Message Received from partition {context.PartitionId}: {data}");
}
await context.CheckpointAsync();
}
}
class Program
{
static void Main(string[] args)
{
string processorHostName = Guid.NewGuid().ToString();
var Options = new EventProcessorOptions()
{
MaxBatchSize = 1,
};
Options.SetExceptionHandler((ex) =>
{
System.Diagnostics.Debug.WriteLine($"Exception : {ex}");
});
var eventHubCS = "event hub connection string";
var storageCS = "storage connection string";
var containerName = "test";
var eventHubname = "test2";
EventProcessorHost eventProcessorHost = new EventProcessorHost(eventHubname, "$Default", eventHubCS, storageCS, containerName);
eventProcessorHost.RegisterEventProcessorAsync<MyEventProcessor>(Options).Wait();
while(true)
{
//do nothing
}
}
}
Related
The idea is to have a worker service doing heavy tasks when requested from an API.
Example of communication:
API: Post data to worker service
Worker service: Post data back when its done.
I've done some research but can't find any solution to what I'm looking for.
Is it possible? If not, there is any other way to do it?
afaik there is no built-in mechanism to do this so you could use a database or queue to request the work to be carried out and then the worker can poll this db/queue to handle the workload.
I use RabbitMQ to communicate between threads in Python on Linux as well as using it for asp.net to communicate with window services (of course running on another thread). The service/worker checks the message queue every iteration/loop and performs work based on what's in the message. The message of course gets pushed into the queue by aspnet, usually through functionality in a controller.
A quick google will get you good results on RabbitMQ and aspnet. Not only can you use this with a worker, but other programs/services you have running on the system.
First link from google that might help you along the way.
https://www.c-sharpcorner.com/article/rabbitmq-message-queue-using-net-core-6-web-api/
I have implemented this into my own project. I have a Service on the aspnet side, where a controller calls it topost messages onto the queue. The Worker checks the queue on every loop and performs functions.
Here is the worker service
public class SchedulerWorkerService : BackgroundService
{
string queueName = "SchedulerQueue";
private readonly IConnectionFactory factory;
private readonly IConnection connection;
private readonly IModel channel;
public SchedulerWorkerService()
{
factory = new ConnectionFactory { HostName = "localhost" };
connection = factory.CreateConnection();
channel = connection.CreateModel();
channel.QueueDeclare(queueName, exclusive: false);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
BasicGetResult result = channel.BasicGet(queueName, true);
if (result != null && result.Body.Length > 0)
{
var message = Encoding.UTF8.GetString(result.Body.ToArray());
// Do you work based on the message here.
switch (message)
{
case "PAUSE SERVICE":
// Stop scheduled task processing
break;
case "REFRESH":
// Perfrom logic to refresh all data stored in variables from database
break;
case "SHUTDOWN":
// perform any shutdown logic to processes
break;
}
}
await Task.Delay(1000, stoppingToken);
}
}
public override void Dispose()
{
channel.Close();
channel.Dispose();
connection.Close();
connection.Dispose();
base.Dispose();
}
}
Here is the service that pushes messages onto the queue
public class SchedulerWorkerCommService
{
public void StopService()
{
}
public void SendMessage(string message)
{
var factory = new ConnectionFactory { HostName = "localhost" };
var connection = factory.CreateConnection();
using var channel = connection.CreateModel();
channel.QueueDeclare("SchedulerQueue", exclusive: false);
channel.BasicPublish("", "SchedulerQueue", body: Encoding.UTF8.GetBytes(message));
}
}
I have a problem where my client websocket implementation within my ASP NET Core web application hosted in an Azure as a App Service stops working after an irregular amount of time.
The implementation is based on a BackgroundService that is run on startup.
public class MyService : BackgroundService
{
public IServiceProvider _services { get; }
public MyService(IServiceProvider services)
{
_services = services;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var fortnoxKeys = new List<string>();
using (var scope = _services.CreateScope())
{
var userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
if (userManager == null)
{
throw e;
}
fortnoxKeys = FortnoxHelper.getAllFortnoxKeys(userManager);
}
var client = new FortnoxWebSocketClient("MY-KEY");
await client.Connect();
await client.AddTenant(fortnoxKeys);
var addTenantResponse = await client.Receive();
await client.AddTopic(WebSocketTopic.Orders);
var addTopicResponse = await client.Receive();
await client.Subscribe();
var subscribeResponse = await client.Receive();
while (!stoppingToken.IsCancellationRequested)
{
var Listen = Task.Run(async () =>
{
while (true)
{
var response = await client.Receive();
if (response.Type == WebSocketResponseType.EventResponse)
{
if (response.EventType == WebSocketEventType.OrderCreated)
{
try
{
// Do work
}
catch (Exception e)
{
continue;
}
// Do more work
}
}
else if (response.Type == WebSocketResponseType.CommandResponse)
{
}
}
});
var Update = Task.Run(async () =>
{
while (true)
{
// Do work and decide which tenants to remove and which to add
await client.RemoveTenant(tenantId);
// ...
await client.AddTenant(newFortnoxKeys);
}
});
Task.WaitAll(Update, Listen);
}
}
}
This service is injected as a singleton within Startup.cs the reason behind it being a singleton is because I want only a single instance of this websocket client to be run.
services.AddSingleton<IHostedService, MyService>();
The idea behind the implementation is for this to run forever. Listening to messages and occasionaly update the websocket within the Update task. However this only works for an irregular amount of time. One time it works for 1 day and next time it works for 20 days before it stops recieving messages.
The FortnoxWebSocketClient is a wrapper for the ClientWebsocket Class and the source code for it can be found here
I have tried upgrading the App Service plan to Standard because I thought it might be the resource recycling in Azure that was the problem but this was unsuccessfull. I have also set the "Always on" flag in Azure to On. In our Azure logs we can see the following message after a certain amount of downtime:
Heartbeat took longer than "00:00:01" at "03/11/2021 04:09:49 +00:00"
I have a library to publish and consume messages from kafka and I am trying to do integration tests on it, but I have a big issue as the consumer "does not" connect properly.
So I reduced (for the test) to the next consumer:
private void FakeConsumer(string kafkaServer)
{
var config = GetSettings(kafkaServer);
using (var c = new ConsumerBuilder<Ignore, string>(config).Build())
{
c.Subscribe(Topic);
CancellationTokenSource cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cts.Cancel();
};
try
{
while (true)
{
try
{
var cr = c.Consume(cts.Token);
Console.WriteLine($"Consumed message '{cr.Value}' at: '{cr.TopicPartitionOffset}'.");
}
catch (ConsumeException e)
{
Console.WriteLine($"Error occurred: {e.Error.Reason}");
}
}
}
catch (OperationCanceledException)
{
// Ensure the consumer leaves the group cleanly and final offsets are committed.
c.Close();
}
}
}
then in my integration test I ran it as background thread as you can see:
[Fact]
public async Task Test()
{
new Thread(() => FakeConsumer()) { IsBackground = true }.Start();
Thread.Sleep(5000);
Publisher<Event> publisher = new Publisher<Event>(args);
Event ev = new Event();
await publisher.PublishAsync(ev, Topic);
Thread.Sleep(10000);
Assert.Single(MessageHandler.Messages);
}
Note: I removed it for the post, but the code has a "messagehandler" which Stores in memory the read message.
I also added some Thread.Sleep on the main test so the topic gets created and there is no "overlaping" between both actions.
the publish code publisher.PublishAsync works perfectly as I can see the message in kafdrop.
The issue is that the code never passes var cr = c.Consume(cts.Token); the thread is stuck in there.
What should I do to receive the information or to allow the BackgroundTrhead to receive the information in var cr = c.Consume(cts.Token);?
Final note: if I split the code into two different console applications it works.
Thanks.
I want my Discord bot to greet members when they join a channel. I have been unable to find an event that fires when this happens. I have tried myClient.UserJoined += MyMethod; and others but they never get fired as I hope. Here is my main code:
public class Program
{
private DiscordSocketClient _client;
private CommandService _commands;
private IServiceProvider _services;
static void Main(string[] args)
=> new Program().RunBotAsync().GetAwaiter().GetResult();
public async Task RunBotAsync()
{
_client = new DiscordSocketClient();
_commands = new CommandService();
_services = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_commands)
.BuildServiceProvider();
string botToken = // removed
_client.Log += Log;
await RegisterCommandsAsync();
await _client.LoginAsync(TokenType.Bot, botToken);
await _client.StartAsync();
await Task.Delay(-1);
}
private Task Log(LogMessage arg)
{
Console.WriteLine(arg);
return Task.CompletedTask;
}
public async Task RegisterCommandsAsync()
{
_client.MessageReceived += HandleCommandAsync;
_client.UserJoined += JoinedAsync; // Something like this to notify bot when someone has joined chat?
await _commands.AddModulesAsync(Assembly.GetEntryAssembly());
}
private Task JoinedAsync(SocketGuildUser arg)
{
throw new NotImplementedException();
}
private async Task HandleCommandAsync(SocketMessage arg)
{
var message = arg as SocketUserMessage;
if(message is null || message.Author.IsBot)
{
return;
}
int argPos = 0;
if (message.HasStringPrefix("!", ref argPos))
{
var context = new SocketCommandContext(_client, message);
await _commands.ExecuteAsync(context, argPos);
}
}
}
Thanks, and let me know if I can provide any more information.
Edit: The suggested link implements the UserJoined event, which only seems to trigger when a new member joins the channel. I need something that triggers everytime anyone logs in to the channel, even existing members.
Judging by the edit, I think you may have a slight mis conception of how the channels work.
Users join a guild, after which, they have become part of the guild.
After they join a guild, they are part of it, and the channels they are allowed to see. Hence there is no need to log into channels anymore.
Now what I think you want to achieve is sending a message in a channel / to a user whenever they switch from the offline state to the online state.
For this you could use the UserUpdated event. Where you can check the previous and the current status of a user, and send a message accordingly.
_client.UserUpdated += async (before, after) =>
{
// Check if the user was offline, and now no longer is
if(before.Status == UserStatus.Offline && after.Status != UserStatus.Offline)
{
// Find some channel to send the message to
var channel = e.Server.FindChannels("Hello-World", ChannelType.Text);
// Send the message you wish to send
await channel.SendMessage(after.Name + " has come online!");
}
}
I am trying to have a console application call Azure mobile services to perform an insert in the DB (a test prototype I am trying out. My eventual goal is to have the console app be run on a regular schedule as an Azure webjob).
The code snippet below does an insert. When I comment out the Console.readline(), the program just runs and exits but does nothing (fails to insert). When I have the readline() in there, it can successfully insert. I am guessing this is because I am calling an async method and control just flows out of main even before async method has a chance to complete.
In the eventual application I am trying to develop, the console app would kick off a lengthy update operation, wait for it to complete and then exit until the azure web jobs scheduler runs it again. What is the recommended way to accomplish the 'await' here?
class Program
{
static IMobileServiceTable<TodoItem> todoTable;
static void Main(string[] args)
{
MobileServiceClient MobileService = new MobileServiceClient(
"mymobileservice url",
"my application ID"
);
todoTable = MobileService.GetTable<TodoItem>();
todoTable.InsertAsync(new TodoItem() { Text = "Console Item 2", Complete = false });
//Console.ReadLine();
}
}
In a Console app, I recommend placing all of your actual logic (including error handling) into a MainAsync method and then calling Task.Wait from Main, as such:
class Program
{
static IMobileServiceTable<TodoItem> todoTable;
static void Main(string[] args)
{
MainAsync(args).Wait();
}
static async Task MainAsync(string[] args)
{
try
{
MobileServiceClient MobileService = new MobileServiceClient(
"mymobileservice url",
"my application ID"
);
todoTable = MobileService.GetTable<TodoItem>();
await todoTable.InsertAsync(new TodoItem() { Text = "Console Item 2", Complete = false });
}
catch (Exception ex)
{
...
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Starting");
Task todo = asyncMethod();
todo.ContinueWith((str) =>
{
Console.WriteLine(str.Status.ToString());
Console.WriteLine("Main end");
});
todo.Wait();
}
public async static Task<string> asyncMethod()
{
MobileServiceClient MobileService = new MobileServiceClient(
"mymobileservice url",
"my application ID"
);
todoTable = MobileService.GetTable<TodoItem>();
await todoTable.InsertAsync(new TodoItem() { Text = "Console Item 2", Complete = false });
return "finished";
}
}
More information can be found here.
It looks like in your console application you really want to wait for the response. In a UI-based application you can't really "wait" for a network operation to finish, otherwise the thread on which it started (the UI thread) will be blocked and the application will appear "hung". But on a console, you can just ask for the .Result property of a Task (or call .Wait()) and the result will be the same:
class Program
{
static IMobileServiceTable<TodoItem> todoTable;
static void Main(string[] args)
{
MobileServiceClient MobileService = new MobileServiceClient(
"mymobileservice url",
"my application ID"
);
todoTable = MobileService.GetTable<TodoItem>();
var item = new TodoItem() { Text = "Console Item 2", Complete = false };
todoTable.InsertAsync(item).Wait();
var itemId = item.Id;
var retrieved = todoTable.LookupAsync(itemId).Result;
//Console.ReadLine();
}
}