BackgroundTask doesn't fire - c#

My background task registers but never fires. I have tried to delete the whole project to erase all tasks, changed the name on the TaskBuilder class, and used different conditions. But nothing seems to work. I sometimes get an error that says it can't show me the error.
Here do I build it:
public async void RegisterBackgroundTask()
{
var taskRegistered = false;
var TaskName = "TimeTriggeredTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == TaskName)
{
taskRegistered = true;
break;
}
}
var tommorowMidnight = DateTime.Today.AddDays(1);
var timeTilMidnight = tommorowMidnight - DateTime.Now;
var minutesTilMidnight = (uint)timeTilMidnight.TotalMinutes;
if (!taskRegistered)
{
var task = RegisterBackgroundTask("TaskBuilderClass",
"TimeTriggeredTask",
new TimeTrigger(minutesTilMidnight, false),
new SystemCondition(SystemConditionType.InternetAvailable));
await task;
CheckPremieres();
}
}
Builder method:
public static async Task<BackgroundTaskRegistration> RegisterBackgroundTask(String taskEntryPoint, String name, IBackgroundTrigger trigger, IBackgroundCondition condition)
{
await BackgroundExecutionManager.RequestAccessAsync();
var builder = new BackgroundTaskBuilder();
builder.Name = name;
builder.TaskEntryPoint = taskEntryPoint;
builder.SetTrigger(trigger);
builder.AddCondition(condition);
BackgroundTaskRegistration task = builder.Register();
//
// Remove previous completion status from local settings.
//
var settings = ApplicationData.Current.LocalSettings;
settings.Values.Remove(name);
return task;
}
This is the task builder class which I also added to the manifest:
public sealed class TaskBuilderClass : IBackgroundTask
{
//
// The Run method is the entry point of a background task.
//
public void Run(IBackgroundTaskInstance taskInstance)
{
//
// Query BackgroundWorkCost
// Guidance: If BackgroundWorkCost is high, then perform only the minimum amount
// of work in the background task and return immediately.
//
var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
var settings = ApplicationData.Current.LocalSettings;
settings.Values["BackgroundWorkCost"] = cost.ToString();
App.nHandler.CheckPremieres();
}
}

I'm pretty sure the task needs to be in its own Windows Runtime Component; I've always done it like this. The Microsoft sample on GitHub also has the tasks in a separate project.
Try doing that. And don't forget to reference the newly created prject from your application. That's the thing that I most always forget.
Once you do that, I also suggest you first trigger the task from Visual Studio Debug Location toolbar, just to make sure everything is configured correctly. It should appear in the dropdown and should work correctly from here, otherwise it won't work when scheduled either.

Related

Not Getting any result in Powershell, Suspecting async issue

Created a PowerShell Cmdlet command in C# and am trying to get the output after passing parameters. The function runs perfectly if I run it as a console application and I get the desired result,But when I am trying to invoke the method from PowerShell, I am not getting any records/results. I suspect there is a async issue which is causing the code to complete, but I am unable to figure it out.
During Debugging I get the status as RanToCompletion in ProcessRecord() Method. Will appreciate some advice on what's wrong in the code below.
protected override void ProcessRecord()
{
var changedFeed = ProcessChangedRecords();
changedFeed.Wait();
}
private async Task ProcessChangedRecords()
{
DaysDuration = DaysDuration * -1;
DateTime particularPointInTime = DateTime.Now.AddDays(DaysDuration).ToUniversalTime();
CosmosClient Client = new CosmosClient("AccountEndpoint = https://test.documents.azure.com:443/;AccountKey=ouJH1chKC5npTfK5y9NUEOA==;");
if (DaysDuration == 0)
{
particularPointInTime = DateTime.Now.ToUniversalTime();
}
var database = Client.GetDatabase("databasename");
var container = database.GetContainer("containername");
var leaseContainer = database.GetContainer("leases");
var cfp = container.GetChangeFeedProcessorBuilder<Document>(ChangeFeedProcessorName, ProcessChanges)
.WithLeaseContainer(leaseContainer)
.WithInstanceName(changeFeedInstanceName)
.WithStartTime(particularPointInTime)
.Build();
await cfp.StartAsync();
await Task.Delay(5000);
await cfp.StopAsync();
}
public async Task ProcessChanges(IReadOnlyCollection<Document> docs, CancellationToken cancellationToken)
{
foreach (var doc in docs)
{
WriteObject(doc, true);
await Task.Delay(1000);
}
}
Just to be more precise, I am trying to use changefeed of CosmosDb and get the modified documents from a particular day. On running the above command, I can see the Lease instance getting created in lease container but there is no output in powershell window

Parallel processing using TPL in windows service

I have a windows service which is consuming a messaging system to fetch messages. I have also created a callback mechanism with the help of Timer class which helps me to check the message after some fixed time to fetch and process. Previously, the service is processing the message one by one. But I want after the message arrives the processing mechanism to execute in parallel. So if the first message arrived it should go for processing on one task and even if the processing is not finished for the first message still after the interval time configured using the callback method (callback is working now) next message should be picked and processed on a different task.
Below is my code:
Task.Factory.StartNew(() =>
{
Subsriber<Message> subsriber = new Subsriber<Message>()
{
Interval = 1000
};
subsriber.Callback(Process, m => m != null);
});
public static void Process(Message message)
{
if (message != null)
{
// Processing logic
}
else
{
}
}
But using the Task Factory I am not able to control the number of tasks in parallel so in my case I want to configure the number of tasks on which messages will run on the availability of the tasks?
Update:
Updated my above code to add multiple tasks
Below is the code:
private static void Main()
{
try
{
int taskCount = 5;
Task.Factory.StartNewAsync(() =>
{
Subscriber<Message> consumer = new
Subcriber<Message>()
{
Interval = 1000
};
consumer.CallBack(Process, msg => msg!=
null);
}, taskCount);
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
public static void StartNewAsync(this TaskFactory
target, Action action, int taskCount)
{
var tasks = new Task[taskCount];
for (int i = 0; i < taskCount; i++)
{
tasks[i] = target.StartNew(action);
}
}
public static void Process(Message message)
{
if (message != null)
{
}
else
{ }
}
}
I think what your looking for will result in quite a large sample. I'm trying just to demonstrate how you would do this with ActionBlock<T>. There's still a lot of unknowns so I left the sample as skeleton you can build off. In the sample the ActionBlock will handle and process in parallel all your messages as they're received from your messaging system
public class Processor
{
private readonly IMessagingSystem _messagingSystem;
private readonly ActionBlock<Message> _handler;
private bool _pollForMessages;
public Processor(IMessagingSystem messagingSystem)
{
_messagingSystem = messagingSystem;
_handler = new ActionBlock<Message>(msg => Process(msg), new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 5 //or any configured value
});
}
public async Task Start()
{
_pollForMessages = true;
while (_pollForMessages)
{
var msg = await _messagingSystem.ReceiveMessageAsync();
await _handler.SendAsync(msg);
}
}
public void Stop()
{
_pollForMessages = false;
}
private void Process(Message message)
{
//handle message
}
}
More Examples
And Ideas
Ok, sorry I'm short on time but here's the general idea/skeleton of what I was thinking as an alternative.
If I'm honest though I think the ActionBlock<T> is the better option as there's just so much done for you, with the only limit being that you can't dynamically scale the amount of work it will do it once, although I think the limit can be quite high. If you get into doing it this way you could have more control or just have a kind of dynamic amount of tasks running but you'll have to do a lot of things manually, e.g if you want to limit the amount of tasks running at a time, you'd have to implement a queueing system (something ActionBlock handles for you) and then maintain it. I guess it depends on how many messages you're receiving and how fast your process handles them.
You'll have to check it out and think of how it could apply to your direct use case as I think some of the details area a little sketchily implemented on my side around the concurrentbag idea.
So the idea behind what I've thrown together here is that you can start any number of tasks, or add to the tasks running or cancel tasks individually by using the collection.
The main thing I think is just making the method that the Callback runs fire off a thread that does the work, instead of subscribing within a separate thread.
I used Task.Factory.StartNew as you did, but stored the returned Task object in an object (TaskInfo) which also had it's CancellationTokenSource, it's Id (assigned externally) as properties, and then added that to a collection of TaskInfo which is a property on the class this is all a part of:
Updated - to avoid this being too confusing i've just updated the code that was here previously.
You'll have to update bits of it and fill in the blanks in places like with whatever you have for my HeartbeatController, and the few events that get called because they're beyond the scope of the question but the idea would be the same.
public class TaskContainer
{
private ConcurrentBag<TaskInfo> Tasks;
public TaskContainer(){
Tasks = new ConcurrentBag<TaskInfo>();
}
//entry point
//UPDATED
public void StartAndMonitor(int processorCount)
{
for (int i = 0; i <= processorCount; i++)
{
Processor task = new Processor(ProcessorId = i);
CreateProcessorTask(task);
}
this.IsRunning = true;
MonitorTasks();
}
private void CreateProcessorTask(Processor processor)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Task taskInstance = Task.Factory.StartNew(
() => processor.Start(cancellationTokenSource.Token)
);
//bind status update event
processor.ProcessorStatusUpdated += ReportProcessorProcess;
Tasks.Add(new ProcessorInfo()
{
ProcessorId = processor.ProcessorId,
Task = taskInstance,
CancellationTokenSource = cancellationTokenSource
});
}
//this method gets called once but the HeartbeatController gets an action as a param that it then
//executes on a timer. I haven't included that but you get the idea
//This method also checks for tasks that have stopped and restarts them if the manifest call says they should be running.
//Will also start any new tasks included in the manifest and stop any that aren't included in the manifest.
internal void MonitorTasks()
{
HeartbeatController.Beat(() =>
{
HeartBeatHappened?.Invoke(this, null);
List<int> tasksToStart = new List<int>();
//this is an api call or whatever drives your config that says what tasks must be running.
var newManifest = this.GetManifest(Properties.Settings.Default.ResourceId);
//task Removed Check - If a Processor is removed from the task pool, cancel it if running and remove it from the Tasks List.
List<int> instanceIds = new List<int>();
newManifest.Processors.ForEach(x => instanceIds.Add(x.ProcessorId));
var removed = Tasks.Select(x => x.ProcessorId).ToList().Except(instanceIds).ToList();
if (removed.Count() > 0)
{
foreach (var extaskId in removed)
{
var task = Tasks.FirstOrDefault(x => x.ProcessorId == extaskId);
task.CancellationTokenSource?.Cancel();
}
}
foreach (var newtask in newManifest.Processors)
{
var oldtask = Tasks.FirstOrDefault(x => x.ProcessorId == newtask.ProcessorId);
//Existing task check
if (oldtask != null && oldtask.Task != null)
{
if (!oldtask.Task.IsCanceled && (oldtask.Task.IsCompleted || oldtask.Task.IsFaulted))
{
var ex = oldtask.Task.Exception;
tasksToStart.Add(oldtask.ProcessorId);
continue;
}
}
else //New task Check
tasksToStart.Add(newtask.ProcessorId);
}
foreach (var item in tasksToStart)
{
var taskToRemove = Tasks.FirstOrDefault(x => x.ProcessorId == item);
if (taskToRemove != null)
Tasks.Remove(taskToRemove);
var task = newManifest.Processors.FirstOrDefault(x => x.ProcessorId == item);
if (task != null)
{
CreateProcessorTask(task);
}
}
});
}
}
//UPDATED
public class Processor{
private int ProcessorId;
private Subsriber<Message> subsriber;
public Processor(int processorId) => ProcessorId = processorId;
public void Start(CancellationToken token)
{
Subsriber<Message> subsriber = new Subsriber<Message>()
{
Interval = 1000
};
subsriber.Callback(Process, m => m != null);
}
private void Process()
{
//do work
}
}
Hope this gives you an idea of how else you can approach your problem and that I didn't miss the point :).
Update
To use events to update progress or which tasks are processing, I'd extract them into their own class, which then has subscribe methods on it, and when creating a new instance of that class, assign the event to a handler in the parent class which can then update your UI or whatever you want it to do with that info.
So the content of Process() would look more like this:
Processor processor = new Processor();
Task task = Task.Factory.StartNew(() => processor.ProcessMessage(cancellationTokenSource.CancellationToken));
processor.StatusUpdated += ReportProcess;

Cant register background task

I have the following background task:
namespace Background
{
public sealed class BackgroundTask : IBackgroundTask
{
private BackgroundTaskDeferral _deferral;
public void Run(IBackgroundTaskInstance taskInstance)
{
//_deferral = taskInstance.GetDeferral();
}
in a file called Task.cs (not that the filename matters).
The Background namespace is in my Solution, which contains a Packaging project, which contains an UWP solution which should launch the Backgroundtask:
private async void LaunchBackground()
{
await BackgroundExecutionManager.RequestAccessAsync();
var exampleTaskName = "Background";
foreach (var taskA in BackgroundTaskRegistration.AllTasks)
{
if (taskA.Value.Name == exampleTaskName)
{
await new ApplicationTrigger().RequestAsync();
break;
}
}
var builder = new BackgroundTaskBuilder();
builder.Name = exampleTaskName;
builder.TaskEntryPoint = "Background.BackgroundTask";
BackgroundTaskRegistration task = builder.Register();
await new ApplicationTrigger().RequestAsync();
}
In the UWP and the Packaging project have the following declared in the manifest:
<Extensions>
<Extension Category="windows.backgroundTasks" EntryPoint="Background.BackgroundTask">
<BackgroundTasks>
<Task Type="systemEvent" />
</BackgroundTasks>
</Extension>
</Extensions>
I could not add a reference to the background project in my packagin project, but did so in my UWP project.
Still, when I try to launch it, I get the following:
At: BackgroundTaskRegistration task = builder.Register(); System.ArgumentException: 'Value does not fall within the expected range.'
builder according to debug is - builder {Windows.ApplicationModel.Background.BackgroundTaskBuilder} Windows.ApplicationModel.Background.BackgroundTaskBuilder
Have I left something out, or why isn't it working?
Edit: After adding a trigger:
builder.SetTrigger(new SystemTrigger(SystemTriggerType.BackgroundWorkCostChange, false));
WhichI actually do not want, I get a new error:
At: await new ApplicationTrigger().RequestAsync(); System.Runtime.InteropServices.COMException: 'Error HRESULT E_FAIL has been returned from a call to a COM component.'
Try the following code , we should check for background task access permissions then Register the background task :
var hasAccess = await BackgroundExecutionManager.RequestAccessAsync();
if (hasAccess == BackgroundAccessStatus.DeniedBySystemPolicy
|| hasAccess == BackgroundAccessStatus.DeniedByUser
|| hasAccess == BackgroundAccessStatus.Unspecified)
{
await new MessageDialog("ACCESS DENIED").ShowAsync();
return;
}
/////////////////////begin registering bg task
var task = new BackgroundTaskBuilder
{
Name = "Background",
TaskEntryPoint = typeof(Background.BackgroundTask).ToString()
};
//Trigger is necessary for registering bg tasks but Conditions are optional
//set a trigger for your bg task to run
//for ex. below Trigger will run when toast Notifications (cleared, user pressed an action button and so on)
ToastNotificationActionTrigger actiontrigger = new ToastNotificationActionTrigger();
task.SetTrigger(actiontrigger);
//Optional Conditions like Internet Connection
//var condition = new SystemCondition(SystemConditionType.InternetAvailable);
//task.AddCondition(condition);//set condition
BackgroundTaskRegistration registration = task.Register();//Register the task
Also we should check if background task is already running before Register it.
Something like below code snippet:
var isAlreadyRegistered = BackgroundTaskRegistration.AllTasks.Any(t => t.Value?.Name == "BackroundTask");
if (isAlreadyRegistered)
{
foreach (var tsk in BackgroundTaskRegistration.AllTasks)
{
if (tsk.Value.Name == "BackroundTask")
{
tsk.Value.Unregister(true);
break;
}
}
}
More information Create and register an out-of-process background task

Activity indicator not loading correctly

I am using an activity indicator to show to the user while the code goes away and calls to azure.
The call itself works fine and all is working well but the activity indicator loads for a set period of time then afterwards the same delay that I'm trying to prevent to the user still takes place then the next screen loads.
I should probably clarify that I'm relatively new to Xamarin and a lot of this async/await stuff alludes me.
I have put the API calls in a Task and put an await method called sleep in there that runs for about 4 seconds. This was the only way I could get it to run the activity indicator. But it looks like this is also causing the problem, so to summarise I'm stuck.
I want the calls to Azure to take place while the activity indicator is going then when they return open the next page, so as to prevent page lagging and freezing. It does not look good.
So, this is the method that calls to the APIs:
private async Task HandleSubmitCommand()
{
//if (IsLoading) return;
IsLoading = true;
await Sleep();
if (!string.IsNullOrEmpty(IdentityDriver))
{
_entryFieldType = "oid";
_statusResponse = DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityDriver, App.TenantId.Value.ToString());
IsLoading = false;
}
else
{
_entryFieldType = "rid";
_statusResponse = DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityRoute, App.TenantId.Value.ToString());
}
if (_statusResponse == "True")
{
Application.Current.MainPage = new DriverDashboardView();
}
else
Application.Current.MainPage = new NotFoundView();
}
This is the sleep method:
private Task Sleep()
{
return Task.Delay(4000);
}
This is the Login method that calls to the API:
public string Login(string ID, string IDPayload, string TenantID)
{
//await Sleep();
var BaseURL = App.ConFigURL;
var URI = string.Format(BaseURL, TenantID);
using (var httpClient = new HttpClient(new HttpClientHandler()))
{
httpClient.BaseAddress = new Uri(URI);
var Telemetry = new { typeid = ID , id = IDPayload};
var Payload = JsonConvert.SerializeObject(Telemetry);
var SAS = DependencyService.Get<ISasToken>().CreateToken(URI, "RootManageSharedAccessKey", "#####################################");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", SAS);
var Content = new StringContent(Payload, Encoding.UTF8, "application/json");
var Response = httpClient.PostAsync(URI,Content).Result;
return Response.IsSuccessStatusCode.ToString();
}
}
As I stated the calls to Azure are fine but it doesn't seem to be running asynchronously. It doesn't help that I'm not at all comfortable with async/await.
An ideas?
I want the calls to Azure to take place while the activity indicator is going then when they return open the next page, so as to prevent page lagging and freezing.
The await Sleep(); under your HandleSubmitCommand method is unnecessary. At this point, your login operation would be executed after 4s. Based on your
Login method, you exexute the login operation synchronously via httpClient.PostAsync(URI,Content).Result. You could modify your methods as follows:
public async Task<bool> Login(string ID, string IDPayload, string TenantID)
{
.
.
var Response = await httpClient.PostAsync(URI,Content);
return Response.IsSuccessStatusCode;
}
private async Task HandleSubmitCommand()
{
IsBusy = true;
//call the login operation asynchronously
_statusResponse = await DependencyService.Get<IWebService>().Login(_entryFieldType, IdentityDriver, App.TenantId.Value.ToString());
IsBusy = false;
}
Note: You need to change the definition for your IWebService interface as follows:
public interface IWebService
{
Task<bool> Login(string ID, string IDPayload, string TenantID);
}

How to Making a Background Task Reschedule Itself

I am trying to make a background task reschedule itself at the end of it's task. but when I go to test it doesn't seem to activate. The task runs initially from the frontend successfully. When I check the lifecycle Events after it finishes I see it's name and two blank ones. When I run the blank ones it runs it, not sure what I am doing that causes them. I am tryin to test with a 16 min time trigger but it doesn't seem to ever run again. This is the code:
var SleepyBand_TaskName = "DataHandlerTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == SleepyBand_TaskName)
{
task.Value.Unregister(true);
}
}
var builder = new BackgroundTaskBuilder();
var trigger = new TimeTrigger(16, false);
builder.Name = SleepyBand_TaskName;
builder.TaskEntryPoint = "SleepyBand_BackgroundTasks.DataHandlerTask";
builder.SetTrigger(trigger);
builder.Register();
TimeTrigger only work for lock-screen apps.
On Windows, a background task will only run using a TimeTrigger if you have requested that your app be placed on the lock screen with a call to RequestAccessAsync and the user accepts the prompt
You would need to use a MaintenanceTrigger
eI would just use System.Threading.Tasks.Task to start threads. Like this you can reshedule a task:
public void Test() {
Task.Run(() => DoSomething());
}
private void DoSomething() {
//Do Something here....
//Do Something again...
Task.Run(() => DoSomething());
}
If you need delays use something like this: https://actionscheduler.codeplex.com/

Categories