Sending scheduled emails by Web API using background tasks - asp.net-web-api

I have researched too much about the ways for sending scheduled emails by .NET core Web API using background tasks. I know it's better that I should implement the background tasks in a windows service which runs separately with app domain.
But my requirement is from web client I will have a table with each row is a promotion event for customer, I can choose to active, pause, stop for each of them, then it will make call to API and from here.
I have to implement each background tasks for each of them that can run synchronous. I have to do that by Web API because end users don't have a place to host the service.
Actual solution:
After one day I came up with the solution which is using IHostedService with BlockingCollection to control the background tasks in runtime as below:
Code for background task using IHostedService:
namespace SimCard.API.Worker
{
internal class TimedHostedService : IHostedService, IDisposable
{
private CancellationTokenSource _tokenSource;
private readonly ILogger _logger;
private Timer _timer;
private readonly TasksToRun tasks;
private readonly IEmailService emailService;
public TimedHostedService(ILogger<TimedHostedService> logger, TasksToRun tasks, IEmailService emailService)
{
this.emailService = emailService;
this.tasks = tasks;
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
tasks.Dequeue();
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
emailService.SendEmail("ptkhuong96#gmail.com", "Test", "OK, Done now");
_logger.LogInformation("Mail sent!");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
}
Here is the code for BlockingCollection:
namespace SimCard.API.Worker
{
public class TasksToRun : ITasksToRun
{
private readonly BlockingCollection<int> _tasks;
public TasksToRun() => _tasks = new BlockingCollection<int>();
public void Enqueue(int settings) => _tasks.Add(settings);
public void Dequeue() => _tasks.Take();
}
}
And the code in controller with get called from web client:
[HttpPost("/api/worker/start")]
public IActionResult Run()
{
tasks.Enqueue(15);
return Ok();
}
Code for Startup.cs:
services.AddHostedService<TimedHostedService>();
services.AddSingleton<TasksToRun, TasksToRun>();
Issue:
After click active button for the first event => controller will get called and one instance of this background task will run. How to pause that task and resume it?
If the first issue is solved, how can I create each background task for each event in the table, think about I may could create more and more event in the future, how can one event get actived, stopped, paused, resumed without affect to another one?
I'm really stuck with this requirement and don't know how to proceed further. If you have a different approach that can adapt my case, you could recommend me also.
Thank you very much for your support.

Related

How to change locale for a conversation in runtme

I'm using botframework composer with multi language and want each user to be able to select preferred language/locale. After resolving the local code for his selection with a choice dialog, how can I set it in conversation so that his locale setting in his device will be overruled for rest of conversation?
Changing locale in emulator works fine, want same behaviour after user selection.
Setting turn.locale works for one turn, but is reset on next turn.
supposing you don't have control over the client, which would be the best.
You can resort to an old overload on the ever-growing hierarchy of bot adapters that hasn't been marked as deprecated.
You'd have to use the PostAsync method (api/post-messages endpoint) in the following controller (showing the one created by the current set of bot framework templates just for comparison):
[Route("api")]
[ApiController]
public class BotController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter StreamingAdapter;
private readonly BotFrameworkAdapter PostAdapter;
private readonly ConversationLocales ConversationLocales;
private readonly IBot Bot;
public BotController(
IBotFrameworkHttpAdapter streamingAdapter,
BotFrameworkAdapter postAdapter,
ConversationLocales conversationLocales,
IBot bot)
{
StreamingAdapter = streamingAdapter;
PostAdapter = postAdapter;
Bot = bot;
}
[HttpPost("messages"), HttpGet("messages")]
public async Task PostOrStreamingAsync()
{
// Delegate the processing of the HTTP POST to the adapter.
// The adapter will invoke the bot.
await StreamingAdapter.ProcessAsync(Request, Response, Bot);
}
[HttpPost("post-messages")]
public async Task<InvokeResponse> PostAsync([FromBody] Activity activity)
{
var savedLocale = ConversationLocales.GetLocaleForConversation(activity.Conversation.Id);
activity.Locale = savedLocale ?? activity.Locale;
return await PostAdapter.ProcessActivityAsync(string.Empty, activity, Bot.OnTurnAsync, default);
}
}
That's supposing you implement a ConversationLocales service that allows you to keep the selected locale for each conversation id.
In the code above we're using the BotFrameworkAdapter adapter instead of IBotFrameworkHttpAdapter, however the AdapterWithErrorHandler used in the templates inherits indirectly from BotFrameworkAdapter, so you could do something like this in ConfigureServices to register "both" adapters:
services.AddSingleton<AdapterWithErrorHandler>();
services.AddSingleton<IBotFrameworkHttpAdapter>(sp => sp.GetRequiredService<AdapterWithErrorHandler>());
services.AddSingleton<BotFrameworkAdapter>(sp => sp.GetRequiredService<AdapterWithErrorHandler>());
To have a single adapter instance.
Using this method the adapter won't be able to use the bot channel streaming endpoints, but that shouldn't be much of a trouble, as long as you don't use the speech client.
You can also read some other details that might be relevan to you in my blog post How does a Bot Builder v4 bot work?, it's a bit dated but still valid.
UPDATE - Found a better solution 😊
This one works with the current wave of adapters and uses the messages pipeline, so it's "modern".
It also requires you to use a custom runtime, that you'll customize as follows.
1 - Create the following middleware
public class LocaleSelectionMiddleware : IMiddleware
{
private readonly IStatePropertyAccessor<string> _userLocale;
public LocaleSelectionMiddleware(UserState userState)
{
_userLocale = userState.CreateProperty<string>("locale");
}
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default)
{
if (turnContext is null)
{
throw new ArgumentNullException(nameof(turnContext));
}
var userLocale = await _userLocale.GetAsync(turnContext, () => turnContext.Activity.Locale);
turnContext.Activity.Locale = userLocale;
(turnContext as TurnContext).Locale = userLocale;
await next(cancellationToken).ConfigureAwait(false);
}
}
2 - Configure the middleware in the adapter in GetBotAdapter() in Startup.cs
public class Startup
{
public Startup(IWebHostEnvironment env, IConfiguration configuration)
{
this.HostingEnvironment = env;
this.Configuration = configuration;
}
//...
public BotFrameworkHttpAdapter GetBotAdapter(IStorage storage, BotSettings settings, UserState userState, ConversationState conversationState, IServiceProvider s)
{
var adapter = IsSkill(settings)
? new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(this.Configuration), s.GetService<AuthenticationConfiguration>())
: new BotFrameworkHttpAdapter(new ConfigurationCredentialProvider(this.Configuration));
adapter
.UseStorage(storage)
.UseBotState(userState, conversationState)
.Use(new RegisterClassMiddleware<IConfiguration>(Configuration))
.Use(new LocaleSelectionMiddleware(userState)) // <-- Add the middleware here
.Use(s.GetService<TelemetryInitializerMiddleware>());
//...
return adapter;
}
//...
}
3 - Set the user.locale property in any dialog
Set the user.locale property from any dialog, and the next turn will have the desired locale, and will be persisted in the user state, until they change it again.

How to exit clean from WebAPI background service

The code below is a Web API that prints on behalf of a SPA. For brevity I've omitted using statements and the actual printing logic. That stuff all works fine. The point of interest is refactoring of the printing logic onto a background thread, with the web api method enqueuing a job. I did this because print jobs sent in quick succession were interfering with each other with only the last job printing.
It solves the problem of serialising print jobs but raises the question of how to detect shutdown and signal the loop to terminate.
namespace WebPrint.Controllers
{
public class LabelController : ApiController
{
static readonly ConcurrentQueue<PrintJob> queue = new ConcurrentQueue<PrintJob>();
static bool running = true;
static LabelController()
{
ThreadPool.QueueUserWorkItem((state) => {
while (running)
{
Thread.Sleep(30);
if (queue.TryDequeue(out PrintJob job))
{
this.Print(job);
}
}
});
}
public void Post([FromBody]PrintJob job)
{
queue.Enqueue(job);
}
}
public class PrintJob
{
public string url { get; set; }
public string html { get; set; }
public string printer { get; set; }
}
}
Given the way I acquire a thread to servicing the print queue, it is almost certainly marked as a background thread and should terminate when the app pool tries to exit, but I am not certain of this, and so I ask you, dear readers, for your collective notion of best practice in such a scenario.
Well, I did ask for best practice.
Nevertheless, I don't have long-running background tasks, I have short-running tasks. They arrive asynchronously on different threads, but must be executed serially and on a single thread because the WinForms printing methods are designed for STA threading.
Matt Lethargic's point about possible job loss is certainly a consideration, but for this case it doesn't matter. Jobs are never queued for more than a few seconds and loss would merely prompt operator retry.
For that matter, using a message queue doesn't solve the problem of "what if someone shuts it down while it's being used" it merely moves it to another piece of software. A lot of message queues aren't persistent, and you wouldn't believe the number of times I've seen someone use MSMQ to solve this problem and then fail to configure it for persistence.
This has been very interesting.
http://thecodelesscode.com/case/156
I would look at your architecture at a higher level, doing 'long running tasks' such as printing should probably live outside of you webapi process entirely.
If this we myself I would:
Create a windows service (or what have you) that has all the printing logic in it, the job of the controller is then to just talk to the service either by http or some kind of queue MSMQ, RabbitMQ, ServiceBus etc.
If via http then the service should internally queue up the print jobs and return 200/201 to the controller as soon as possible (before printing happens) so that the controller can return to the client efficiently and release it's resources.
If via a queuing technology then the controller should place a message on the queue and again return 200/201 as quick as possible, the service can then read the messages at it's own rate and print one at a time.
Doing it this way removes overhead from your api and also the possibility of losing print jobs in the case of a failure in the webapi (if the api crashes any background threads may/will be effected). Also what if you do a deployment at the point of someone printing, there's a high chance the print job will fail.
My 2 cents worth
I believe that the desired behavior is not something that should be done within a Controller.
public interface IPrintAgent {
void Enqueue(PrintJob job);
void Cancel();
}
The above abstraction can be implemented and injected into the controller using the frameworks IDependencyResolver
public class LabelController : ApiController {
private IPrintAgent agent;
public LabelController(IPrintAgent agent) {
this.agent = agent;
}
[HttpPost]
public IHttpActionResult Post([FromBody]PrintJob job) {
if (ModelState.IsValid) {
agent.Enqueue(job);
return Ok();
}
return BadRequest(ModelState);
}
}
The sole job of the controller in the above scenario is to queue the job.
Now with that aspect out of the way I will focus on the main part of the question.
As already mentioned by others, there are many ways to achieve the desired behavior
A simple in memory implementation can look like
public class DefaultPrintAgent : IPrintAgent {
static readonly ConcurrentQueue<PrintJob> queue = new ConcurrentQueue<PrintJob>();
static object syncLock = new Object();
static bool idle = true;
static CancellationTokenSource cts = new CancellationTokenSource();
static DefaultPrintAgent() {
checkQueue += OnCheckQueue;
}
private static event EventHandler checkQueue = delegate { };
private static async void OnCheckQueue(object sender, EventArgs args) {
cts = new CancellationTokenSource();
PrintJob job = null;
while (!queue.IsEmpty && queue.TryDequeue(out job)) {
await Print(job);
if (cts.IsCancellationRequested) {
break;
}
}
idle = true;
}
public void Enqueue(PrintJob job) {
queue.Enqueue(job);
if (idle) {
lock (syncLock) {
if (idle) {
idle = false;
checkQueue(this, EventArgs.Empty);
}
}
}
}
public void Cancel() {
if (!cts.IsCancellationRequested)
cts.Cancel();
}
static Task Print(PrintJob job) {
//...print job
}
}
which takes advantage of async event handlers to process the queue in sequence as jobs are added.
The Cancel is provided so that the process can be short circuited as needed.
Like in Application_End event as suggested by another user
var agent = new DefaultPrintAgent();
agent.Cancel();
or manually by exposing an endpoint if so desired.

BroadcastReceiver does not work when application is in background or killed

I have created BroadcastReceiver where I detect when an incoming call is received on the device.
My code is
[BroadcastReceiver()]
[IntentFilter(new[] { "android.intent.action.PHONE_STATE" })]
public class MyBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
//My implementation
}
}
Problem is when the application is not running or killed forcefully, BroadcastReceiver class does not get called.
Can anyone please help?
BroadcastReceiver does not work when application is not in background or killed
You could refer to: Android Broadcast Receiver not working when the app is killed, as CommonsWare said:
Once onReceive() returns, if you do not have an activity in the foreground and you do not have a running service, your process importance will drop to what the documentation refers to as a "cached process". Your process is eligible to be terminated at any point. Once your process is terminated, your BroadcastReceiver goes away. Hence, your code as written will be unreliable, as your process might be terminated within your 30-second window.
Solution:
So you could use a Service to implement your feature, here is my simple demo:
[Service]
[IntentFilter(new String[] { "com.xamarin.DemoService" })]
public class DemoService : Service
{
private static DemoReceiver m_ScreenOffReceiver;
public override IBinder OnBind(Intent intent)
{
return null;
}
public override void OnCreate()
{
registerScreenOffReceiver();
base.OnCreate();
}
public override void OnDestroy()
{
UnregisterReceiver(m_ScreenOffReceiver);
m_ScreenOffReceiver = null;
base.OnDestroy();
}
//From this thread: https://stackoverflow.com/questions/20592366/the-process-of-the-service-is-killed-after-the-application-is-removed-from-the-a
public override void OnTaskRemoved(Intent rootIntent)
{
Intent restartServiceIntent = new Intent(Application.Context, typeof(DemoService));
restartServiceIntent.SetPackage(PackageName);
PendingIntent restartServicePendingIntent = PendingIntent.GetService(Application.Context, 1, restartServiceIntent, PendingIntentFlags.OneShot);
AlarmManager alarmService = (AlarmManager)Application.Context.GetSystemService(Context.AlarmService);
alarmService.Set(AlarmType.ElapsedRealtime, SystemClock.ElapsedRealtime() + 1000, restartServicePendingIntent);
base.OnTaskRemoved(rootIntent);
}
private void registerScreenOffReceiver()
{
m_ScreenOffReceiver = new DemoReceiver();
IntentFilter filter = new IntentFilter("com.xamarin.example.TEST");
RegisterReceiver(m_ScreenOffReceiver, filter);
}
}
Update:
If you need the service run at a higher priority to avoid it be killed, you could try using Foreground Service. As SushiHangover said:
As a foreground service it has a higher priority so the OS will consider it last to be killed, it avoids the automatic dozing of your services to save battery in later APIs , etc... The "downside" is the user must be made aware that it is running, thus the requirement to be placed in the notification bar, personally I do not see that as a problem and wish it was a hard requirement for all services.
BroadcastReceiver will not work when the application is killed, you have to use service for that , you can use BroadcastReceiver inside a service to run it even when the app is not running.

Background execution of Spring services

I'm developing a simple Spring MVC application to download tweets from the streaming API and show them in a webpage. Users of the application can submit a Task with the keywords of the tweets that they want to download. This tasks are shared so everyone can start, stop, modify, change or cancel a task.
TwitterFetcher is the class responsible of download tweets. This class receives a Task and persists all tweets downloaded in a database.
#Service
public class TwitterFetcher {
#Autowired
private OAuthService oAuthService;
#Autowired
private TweetService tweetService;
private Task task;
private TwitterStream twitterStream;
public void start(Task task) {
/* Stop previous stream */
stop();
/* Get OAuth credentials */
OAuth oAuth = oAuthService.findOneEnabled();
if (oAuth == null) {
} else {
this.task = task;
Configuration oAuthConfiguration = getOAuthConfiguration(oAuth);
twitterStream = new TwitterStreamFactory(oAuthConfiguration).getInstance();
twitterStream.addListener(new TwitterListener());
String keywords = task.getBaseKeywords() + ", " + task.getExpandedKeywords();
FilterQuery filterQuery = new FilterQuery();
filterQuery.track(keywords.split(", "));
twitterStream.filter(filterQuery);
}
}
public void stop() {
if (twitterStream != null) {
twitterStream.shutdown();
}
}
private Configuration getOAuthConfiguration(OAuth oAuth) {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(false);
cb.setJSONStoreEnabled(true);
cb.setOAuthAccessToken(oAuth.getAccessToken());
cb.setOAuthAccessTokenSecret(oAuth.getAccessTokenSecret());
cb.setOAuthConsumerKey(oAuth.getConsumerKey());
cb.setOAuthConsumerSecret(oAuth.getConsumerSecret());
return cb.build();
}
private class TwitterListener implements StatusListener {
#Override
public void onStatus(Status status) {
/* Persist new tweet */
Tweet tweet = new Tweet();
tweet.setJson(DataObjectFactory.getRawJSON(status));
tweetService.save(tweet);
}
[Omitted code]
}
}
The basic functionality would be the next one:
A user start the fetcher from the website.
The fetcher receives a new tweet and it's saved in the DB
The fetcher keeps receiving tweets until a user stop it.
The application has a dashboard to control the fetchers and the tasks and the users must be able to interact with it while the fetcher is downloading.
My question is, Would the fetcher block the app or will be executed in a different thread? In the worst case, what I have to change to solve this? I'm still far from an usable app so I can't test it. Even so, I want to fix it right now if possible.
You can use ExecutorService to run the fetcher in a separate thread. I'd recommend using ThreadPool so you don't blow performance if too many users running the fetcher:
ExecutorService executor = Executors.newFixedThreadPool(maxThreads)
When a task is submitted through the executor it will return a Future object from which you can check for job completion
Future f = executor.submit(myTask);
boolean isDone = f.isDone();
Please read more about Java concurrency if you're not familiar: http://docs.oracle.com/javase/tutorial/essential/concurrency/index.html
Annotate your start() method with #Async.
#Async
public void start(Task task)
This will make the start method asynchronous and will not block the application.
You can check out a simple example here.

Live SDK Windows Phone 7 GetCompleted callback in UI thread?

public class SyncHelper
{
private LiveConnectClient client;
public event EventHandler SyncStarted;
public event EventHandler SyncCompleted;
public SyncHelper(LiveConnectClient client)
{
this.client = client;
}
public void TrySync()
{
Debug.WriteLine("Beginning sync");
OnSyncStarted();
client.GetCompleted += OnGetCompleted;
client.GetAsync("me/skydrive/files");
}
private void OnGetCompleted(object sender, LiveOperationCompletedEventArgs e)
{
Thread.Sleep(10000);
Debug.WriteLine("Get Completed");
client.GetCompleted -= OnGetCompleted;
OnSyncCompleted();
Debug.WriteLine("Sync completed");
}
private void OnSyncStarted()
{
if (SyncStarted != null)
SyncStarted(this, new EventArgs());
}
private void OnSyncCompleted()
{
if (SyncCompleted != null)
SyncCompleted(this, new EventArgs());
}
}
The function OnGetCompleted is being called in the UI thread and the UI is unresponsive. From whatever I know, I thought these callbacks would work in a different thread and we would have to use the displatcher to post it to the UI thread. Any thoughts? Help!
The GetAsync call is likely using a background thread to do the actual fetch, but then it's trying to help you by calling the Completed callback in the original thread context so you don't have to use a Dispatcher.
Why are you putting in a Sleep(10000) anyway? The callback says "hey, I'm done". At that point you should update the UI if you want. If you need to do further processing that takes significant time, spawn a background thread, threadpool task or use another asynchronous call with another callback.

Resources