How to create a Task Scheduler App - c#

I have been task with (ha) creating an application that will allow the users to schedule a command line app we have with a parameter.
So the command line app takes an xml and "runs it"
So bottom line I either need to create a windows service or learn how to interact with the Task Scheduler service already running on the box (version 1 Xp /2003)
At first I though it would be easy have a service run and when a job is submitted, calculate the time between now and run and set up a timer to wait that amount of time. This is better then checking every minute if it's time to run.
Were I hit a wall is I relized I do not know how to communicate with a running windows service. Except maybe create a file with details and have the service with a file watcher to load the file and modify the schedule.
So the underlying questions are how can I execute this psedo code
from client
serviceThatIsRunning.Add(Job)
Or ineracting with the task schedule or creating .job files using c# 3.5
Edit:
To clarify I created a small sample to get my thoughts on "paper"
So I have a Job Class
public class Job
{
#region Properties
public string JobName { get; set; }
public string JobXML { get; set; }
private Timer _JobTimer;
public Timer JobTimer
{
get
{
return _JobTimer;
}
}
#endregion
public void SetJobTimer(TimeSpan time)
{
if (_JobTimer != null)
{
_JobTimer.Dispose();
}
_JobTimer = new Timer(new TimerCallback(RunJob), null, time, time);
}
private void RunJob(Object state)
{
System.Diagnostics.Debug.WriteLine(String.Format("The {0} Job would have ran with file {1}", JobName, JobXML));
}
public override string ToString()
{
return JobName;
}
public void StopTimer()
{
_JobTimer.Dispose();
}
}
Now I need to create an App to house these Jobs that is constantly running, that is why I though of Windows Services, and then a Windows app to allow the user to work with the Job List.
So the question is if I create a Windows Service how do I interact with methods in that service so I can change the JobList, add, delete, change.
Here is a small windows app I created to show that the Job class does run. Interesting point, If I am doing this correctly, I do not add the Job to a listbox and the Add method exits the Job Timer portion still runs and does not get picked up by the Garbage Collector.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnAddJob_Click(object sender, EventArgs e)
{
Job job = new Job();
job.JobName = txtJobName.Text;
job.JobXML = txtJobXML.Text;
job.SetJobTimer(new TimeSpan(0, 0, Convert.ToInt32(JobTime.Value)));
// ??Even If I don't add the Job to a list or ListBox it seems
// ??to stay alive and not picked up by the GC
listBox1.Items.Add(job);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex > -1)
{
Job job = listBox1.Items[listBox1.SelectedIndex] as Job;
txtJobName.Text = job.JobName;
txtJobXML.Text = job.JobXML;
}
}
private void btnRemove_Click(object sender, EventArgs e)
{
Job job = listBox1.Items[listBox1.SelectedIndex] as Job;
job.StopTimer();
listBox1.Items.Remove(job);
}
private void btnCollect_Click(object sender, EventArgs e)
{
GC.Collect();
}
}

If you want to schedule a task using the task scheduler it could be as simple as below. You just need to customize the command line arguments that you pass to schtasks for your needs. See this link for a detailed explanation of command line arguments.
Process p = Process.Start("schtasks", commandArgs);
p.WaitForExit();

If you want to start multiple tasks that run at different time intervals, you can
create for instance a class JobThread that defines a timer that is initialized using the Initialize method:
m_timer = new Timer(new TimerCallback(this.timerHandler), null, this.Interval, this.Interval);
Furthermore, this class defines a List of Job objects. These jobs are executed from the timerHandler.
Finally, you create a singleton JobManager class that defines a Start and Stop method.
In the Start method you do something like this:
foreach (var jobThread in this.m_jobThreads)
{
jobThread.Initialize();
}
This JobManager has also a Initiliaze method that accepts a XmlNode parameter. This method will parse the Xml-job you pass from the command-line.

There was an answer on this thread that is no longer there but, I am going to try to create a listener by keeping a port open
WCF through Windows Services
http://msdn.microsoft.com/en-us/library/ms733069.aspx
Also adding the attribute
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
Helps to keep state of the service.

Related

How to efficiently count HTTP Calls in asp.net core?

I have an abstract class called HttpHelper it has basic methods like, GET, POST, PATCH, PUT
What I need to achieve is this:
Store the url, time & date in the database each time the function is called GET, POST, PATCH, PUT
I don't want to store directly to the database each time the functions are called (that would be slow) but to put it somewhere (like a static queue-memory-cache) which must be faster and non blocking, and have a background long running process that will look into this cache-storage-like which will then store the values in the database.
I have no clear idea how to do this but the main purpose of doing so is to take the count of each calls per hour or day, by domain, resource and url query.
I'm thinking if I could do the following:
Create a static class which uses ConcurrentQueue<T> to store data and call that class in each function inside HttpHelper class
Create a background task similar to this: Asp.Net core long running/background task
Or use Hangfire, but that might be too much for simple task
Or is there a built-in method for this in .netcore?
Both Hangfire and background tasks would do the trick as consumers of the queue items.
Hangfire was there before long running background tasks (pre .net core), so go with the long running tasks for net core implementations.
There is a but here though.
How important is to you that you will not miss a call? If it is, then neither can help you.
The Queue or whatever static construct you have will be deleted the time your application crashes/machine restarts or just plain recycling of the application pools.
You need to consider some kind of external Queuing mechanism like rabbit mq with persistence on.
You can also append to a file, but that might also cause some delays as read/write.
I do not know how complex your problem is but I would consider two solutions.
First is calling Async Insert Method which will not block your main thread but will start task. You can return response without waiting for your log to be appended to database. Since you want it to be implemented in only some methods, I would do it using Attributes and Middleware.
Simplified example:
public IActionResult SomePostMethod()
{
LogActionAsync("This Is Post Method");
return StatusCode(201);
}
public static Task LogActionAsync(string someParameter)
{
return Task.Run(() => {
// Communicate with database (X ms)
});
}
Better solution is creating buffer which will not communicate with database each time but only when filled or at interval. It would look like this:
public IActionResult SomePostMethod()
{
APILog.Log(new APILog.Item() { Date = DateTime.Now, Item1 = "Something" });
return StatusCode(201);
}
public partial class APILog
{
private static List<APILog.Item> _buffer = null;
private cont int _msTimeout = 60000; // Timeout between updates
private static object _updateLock = new object();
static APILog()
{
StartDBUpdateLoopAsync();
}
private void StartDBUpdateLoopAsync()
{
// check if it has been already and other stuff
Task.Run(() => {
while(true) // Do not use true but some other expression that is telling you if your application is running.
{
Thread.Sleep(60000);
lock(_updateLock)
{
foreach(APILog.Item item in _buffer)
{
//Import into database here
}
}
}
});
}
public static void Log(APILog.Item item)
{
lock(_updateLock)
{
if(_buffer == null)
_buffer = new List<APILog.Item>();
_buffer.Add(item);
}
}
}
public partial class APILog
{
public class Item
{
public string Item1 { get; set; }
public DateTime Date { get; set; }
}
}
Also in this second example I would not call APILog.Log() each time but use Middleware in combination with Attribute

Blazor (Server) scoped object in dependency injection creating multiple instances

For demonstration purposes let's say I have a class called StateManager:
public class StateManager
{
public StateManager()
{
IsRunning = false;
}
public void Initialize()
{
Id = Guid.NewGuid().ToString();
IsRunning = true;
KeepSession();
}
public void Dispose()
{
Id = null;
IsRunning = false;
}
public string Id { get; private set; }
public bool IsRunning { get; private set; }
private async void KeepSession()
{
while(IsRunning)
{
Console.WriteLine($"{Id} checking in...");
await Task.Delay(5000);
}
}
}
It has a method that runs after it is initiated that writes it's Id to the console every 5 seconds.
In my Startup class I add it as a Scoped service:
services.AddScoped<StateManager>();
Maybe I am using the wrong location but in my MainLayout.razor file I am initializing it on OnInitializedAsync()
#inject Models.StateManager StateManager
...
#code{
protected override async Task OnInitializedAsync()
{
StateManager.Initialize();
}
}
When running the application after it renders the first page the console output is showing that there are 2 instances running:
bcf76a96-e343-4186-bda8-f7622f18fb27 checking in...
e5c9824b-8c93-45e7-a5c3-6498b19ed647 checking in...
If I run Dispose() on the object it ends the KeepSession() while loop on one of the instances but the other keeps running. If I run Initialize() a new instance appears and every time I run Initialize() new instances are generated and they are all writing to the console with their unique id's. I am able to create as many as I want without limit.
I thought injecting a Scoped<> service into the DI guaranteed a single instance of that object per circuit? I also tried initializing within the OnAfterRender() override in case the pre-rendering process was creating dual instances (although this does not explain why I can create so many within a page that has the service injected).
Is there something I am not handling properly? Is there a better location to initialize the StateManager aside from MainLayout?
I also tried initializing within the OnAfterRender() override in case the pre-rendering process was creating dual instances
It is caused by pre-rendering & the StateManager is not disposed.
But you cannot avoid it by putting the initialization within OnAfterRender(). An easy way is to use the RenderMode.Server instead.
<app>
#(await Html.RenderComponentAsync<App>(RenderMode.ServerPrerendered))
#(await Html.RenderComponentAsync<App>(RenderMode.Server))
</app>
Since your StateManager requires a knowledge on StateManagerEx, let's firstly take a dummy StateManagerEx as an example, which is easier than your scenario:
public class StateManagerEx
{
public StateManagerEx()
{
this.Id = Guid.NewGuid().ToString();
}
public string Id { get; private set; }
}
When you render it in Layout in RenderMode.Server Mode:
<p> #StateManagerEx.Id </p>
You'll get the Id only once. However, if you render it in RenderMode.ServerPrerendered mode, you'll find that:
When browser sends a request to server ( but before Blazor connection has been established), the server pre-renders the App and returns a HTTP response. This is the first time the StateManagerEx is created.
And then after the Blazor connection is established, another StateManagerEx is created.
I create a screen recording and increase the duration of each frame by +100ms, you can see that its behavior is exactly the same as what we describe above (The Id gets changed):
The same goes for the StateManager. When you render in ServerPrerendered mode, there will be two StateManager, one is created before the Blazor connection has been established, and the other one resides in the circuit. So you'll see two instances running.
If I run Initialize() a new instance appears and every time I run Initialize() new instances are generated and they are all writing to the console with their unique id's.
Whenever you run Initialize(), a new Guid is created. However, the StateManager instance keeps the same ( while StateManager.Id is changed by Initialize()).
Is there something I am not handling properly?
Your StateManager did not implements the IDisposable. If I change the class as below:
public class StateManager : IDisposable
{
...
}
even if I render the App in ServerPrerendered mode, there's only one 91238a28-9332-4860-b466-a30f8afa5173 checking in... per connection at the same time:

How to run a foreground scheduled task in ASP.NET MVC?

I've this code in my ASP.NET MVC application to share a value across the application.
public static class Global
{
public static string Token { get; private set; };
public static void LoadFromFile()
{
// loads Token value from a settings file
}
}
What I want to do is to run LoadFromFile method once a day to update the Token value.
I can't use a separate background task like in HangFire, since I want to update the value for current running application.
How can I do it? thanks.
Update:
Mates who think this is a duplicate, please read the question. I want to update the shared value in current running application. changing it in in a separate background task won't change it for current application.
To help SO wandering polices rest a while, I got the answer.
I can run a scheduled background task to access an endpoint in my site, and from there update the static Token value.
I had faced similar requirement once, here is the trick I used
private static DateTime lastRunAt;
private static object loadingTokenLock = new object();
private static bool TokenUpdateNeeded
{
get
{
return DateTime.UtcNow.DayOfYear != lastRunAt.DayOfYear;
}
}
public static void TryLoadToken()
{
if (TokenUpdateNeeded)
lock (loadingTokenLock)
if (TokenUpdateNeeded)
LoadFromFile();
}
public static void LoadFromFile()
{
// loads Token value from a settings file
lastRunAt = DateTime.UtcNow;
}
void Session_Start(object sender, EventArgs e)
{
TryLoadToken();
}
I can't remember the exact coding, but the idea was to update the Token upon the first request of the day.
The problem is if your application have not been visit for more than a day, the Token will not be updated. So we defined a task in Windows Task Scheduler to visit the site once everyday

C# Waiting for an action in a parallel thread

I have a C# form application to send CAN messages.
At the start, I start a thread which monitors incoming messages, and displays them on a DataGridView.
Currently the app is set so that the user can send a CAN message one at time, and view its response.
All this works fine, and reliably.
Phase 2 of my application requires me to send a full list of CAN messages automatically.
For this I need to send a CAN message, and wait for its response, and then send the next.
How can I monitor the receive thread, and halt my send function long enough for the response to be received?
As the send message is not in a separate thread, Thread.Sleep(n) halts the entire program.
My approach:
public class DataViewModel
{
public AsynchronousCommand Send;
public DataViewModel()
{
Send = new AsynchronousCommand(() =>
{
SendData();
});
}
private void SendData()
{
}
}
public class SomewhereInForm
{
private DataViewModel dataViewModel = new DataViewModel();
public SomewhereInForm()
{
dataViewModel.Send.Executed += SendOnExecuted;
}
private void SendOnExecuted(object sender, CommandEventArgs args)
{
}
private void DoSome()
{
dataViewModel.Send.DoExecute(new int());
}
}
It's example with 1 command. You can put commands in List, whatever.
If you don't want build your own library witch commands, use this: Commands in MVVM

Share an object instance between multiple session/application instances in ASP.NET MVC

I am developing a project for a log monitor and I am using an ASP.NET application with SignalR.
The main objective of the application is to provide a monitor of error logs in multiple clients across different locations (LCD monitors). Every moment when a log error is created in database, the application should notify all the clients with the new error.
I am wondering to create a static Timer variable in the web application, that will be started by the Application_Start method.
But, knowing the application will have a single thread per session, I think the web server will have a lot of timers running together.
I need to know how to make this Timer instance unique for all the session instances in the web server.
Application_Start is not triggered by a new session, but by the start of the application. If you initialize your timer in Application_Start, you don't need to worry about multiple timer instances.
You can create an instance class that has a timer.
For instance:
public class MyTimerHolder
{
private static Lazy<MyTimerHolder> _instance = new Lazy<MyTimerHolder>(() => new MyTimerHolder());
private readonly TimeSpan _checkPeriod = TimeSpan.FromSeconds(3);
private IHubContext _hubProxy;
// Threaded timer
private Timer _timer;
public MyTimerHolder()
{
_timer = new Timer(CheckDB, null, _checkPeriod, _checkPeriod);
}
public void BroadcastToHub(IHubContext context)
{
_hubProxy = context;
}
public void CheckDB(object state)
{
if (_hubProxy != null)
{
// Logic to check your database
_hubProxy.Clients.All.foo("Whatever data you want to pass");
}
}
public static MyTimerHolder Instance
{
get
{
return _instance.Value;
}
}
}
Then you can change the hubContext at any point from any method. So lets say you want to broadcast to clients connected to hub "MyDBCheckHub". At any point in your application all you have to do is:
MyTimerHolder.Instance.BroadcastToHub(GlobalHost.ConnectionManager.GetHubContext<MyDBCheckHub>());
You could throw this in your application start or wherever you please, there'll only be 1 instance of MyTimerHolder within the app domain.

Categories