ASP.NET App that monitors an external sensor (Multithreading) - c#

I created an MVC 4 App that gets data from some external sensors, and then shows data depending on the values recived. The external sensors expose their values through an http page (e.g. http:///CheckValue). The MVC App must be continiously checking for those values, let´s say every 5 seconds.
The basic Idea is that this process must be done on the background and in a infinite loop, each sensor on a different thread.
The problem is that I don´t know where is the best place to do this, as of now I just create a new Task for each sensor at the Application_Start method on the Global.aspx file.
protected void Application_Start()
{
foreach (var sensor in sensors)
{
Task.Factory.StartNew(() => sensor.readValue(5000));
}
}
This is the code for readValue()
public void readValue(int timespan)
{
try
{
using HttpClient client = new HttpClient())
{
while(true){
try
{
string result = await client.GetStringAsync(url);
//result validation logic
}
catch(Exception)
{
//Exception Handling
}
Thread.Sleep(timespan);
}
}
}catch(Exception e)
{
Debug.Write(e.Message);
}
}
I´m new to ASP.NET so I really don´t know if this should be in the Application_Start method, or if maybe it shouldn´t be on the MVC App at all, and do it on a separate Windows Service (If so how do I send the values back to the MVC App)

Related

Redirect to a different aspx page and run the next code in background (.NET 4.5.2)

I am working on an ASP.NET Webform project (legacy code).On my button_click event i am sending sms message to all the datas populated in this.
var customerSMS = BusinessLayer.SMS.SmsSetup.GetAllCustomerSMS(OfficeId);
This takes around 15seconds to do all the computing and get the data(1000rows)
from the Db.And for each data it runs through the loop and does validation and
sends the sms and it does take time.I want to do this task in background and
redirect the user to the index page and the background process continues till it
gets out of the loop.I am new to this and still learning this beautiful
language C#.I did go through this amazing Asynchronous Programming async/await
and Multithreading approach and got hold of it only in simple WindowsForm
applications.Any reference/code snippet/best approach with a simple explanation for my case would be helpful.
My button click event code :
protected void ReturntoDashboard_Click(object sender, EventArgs e)
{
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
if (sms.EnableSmsData && sms.SmsCount > 0)
{
#region Loan Section
var smsLoan = Everest.Net.BusinessLayer.SMS.SmsSetup.GetLoanId(s.Sms_AccountNumber);
var loanId =
BusinessLayer.SMS.SmsSetup.GetLoanIdValue(s.Sms_AccountNumber);
var dateexceeded =
BusinessLayer.SMS.SmsSetup.IsDateExceeded(loanId);
if (smsLoan != null && dateexceeded == true)
{
foreach (Common.SMS.SMSSetup sm in smsLoan)
{
var smsClosingBalanceLoan = BusinessLayer.SMS.SmsSetup.GetAmountForLoanAlert( sm.LoanId,
BusinessLayer.Core.DateConversion
.GetCurrentServerDate()
.AddDays(sms.DaysbeforeLoanalerts).ToString());
if (smsClosingBalanceLoan != null)
{
if (smsClosingBalanceLoan.LoanAmountToPay > 0)
{
int smsSentAlertCount = sms.LoanAlertCount;
var logCount = BusinessLayer.SMS.SmsSetup.GetLoanSmsAlertSentCount(DateTime.Now.AddDays(-smsSentAlertCount).ToString("yyyy-MM-dd"), DateTime.Now.ToString("yyyy-MM-dd"), sm.LoanAccountNumber);
if (logCount < smsSentAlertCount)
{
smsLog = new Everest.Net.Common.SMS.SMSSetup();
finalMessage = "Dear Member, Your Loan accnt " + sm.LoanAccountNumber + " with Principal"+ "+" + "Int Amnt: Rs." + smsClosingBalanceLoan.LoanAmountToPay + " need to be payed.Thank You," + officeName.OfficeName;
smsLog.LogServiceType = "Loan";
smsLog.LogSmsType = s.Sms_SmsType;
smsLog.LogSmsMessage = finalMessage;
smsLog.LogCustomerId = s.CustomerId.ToString();
smsLog.LogAccountNumber = s.Sms_AccountNumber;
smsLog.LogAccountType = s.Sms_AccountType;
smsLog.LogSmsSentDate = BusinessLayer.Core.DateConversion.GetCurrentServerDate();
smsLog.LogSmsFailedDate = "";
smsLog.LogSentStatus = true;
smsLog.LogUserId = UserId;
smsLog.LogSmsFailedMessage = "";
try
{
var result = Everest.Net.BusinessLayer.SMS.smsParameters.SendSMS(sms.FromNum, sms.Token, sms.Url, cellNum, finalMessage);
}
catch (Exception ex)
{
smsLog.LogSmsFailedDate = System.DateTime.Now.ToString("MM/dd/yyyy HHmmss");
smsLog.LogSentStatus = false;
smsLog.LogSmsFailedMessage = ex.Message;
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
sms.SmsCount = sms.SmsCount - 1;
Everest.Net.BusinessLayer.SMS.SmsSetup.UpdateSmsSetup(sms);
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
}
}
}
}
}
}
}
catch (Exception ex)
The ideal solution would remove the responsibility of sending the SMS from the web application itself. Instead, the web application should create a database record containing the message and recipient addresses, and a separate background job (e.g. a Windows Service) should poll the database and send SMS messages when neeeded. This is the best solution in terms of fault tolerance and auditability, because there is a permanent record of the messaging job which can be resumed if the system fails.
That being said, maybe you don't want to go to all that trouble. If you feel strongly that you wish to send the SMS directly from the ASP.NET application, you will need to create a Task and queue it to run using QueueBackgroundWorkitem. You will need to refactor your code a bit.
Move all the logic for sending the SMS into a separate function that accepts all the information needed as parameters. For example,
static void SendSMS(string[] addresses, string messagetext)
{
//Put your SMS code here
}
When you need to call the function, queue it as a background item
HostingEnvironment.QueueBackgroundWorkItem(a => SendSMS(addresses, messageText));
If your worker task needs to access its own cancellation token (e.g. if it is supposed to loop until cancelled), it is passed as an argument to the lambda expression. So you could modify the prototype
static void SendSMS(string[] addresses, string messagetext, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
//Put your code here
}
}
and pass it thus:
HostingEnvironment.QueueBackgroundWorkItem(token => SendSMS(addresses, messageText, token));
Placing the task in the background queue ensures that ASP.NET keeps track of the thread, doesn't try to garbage collect it, and shuts it down properly when the application pool needs to shut down.
After queuing the background operation, your page can render is content per usual and conclude the HTTP response while the task continues to execute.

How to perform database operation independently?

I have 1 exe which is nothing bit a Windows form which will continuously run in background and will watch my serial port and I have 1 event data receive event which fires as my serial port receive data.
As soon as I receive data in this event I will pass this data to another event handler which saves this data in database through web api method.
But data to my serial port will be coming frequently so I want to save this data to my database independently so that my database insert operation doesn't block my incoming serial port data.
This is my code:
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)//Fires as my serial port receives data
{
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
// Send data to whom ever interested
if (NewSerialDataRecieved != null)
{
NewSerialDataRecieved(this, new SerialDataEventArgs(data)); //pass serial port data to new below event handler.
}
}
void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e) //I want this event handler to run independently so that database save operation doenst block incoming serial port data
{
if (this.InvokeRequired)
{
// Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
return;
}
//data is converted to text
string str = Encoding.ASCII.GetString(e.Data);
if (!string.IsNullOrEmpty(str))
{
//This is where i will save data to through my web api method.
RunAsync(str).Wait();
}
}
static async Task RunAsync(string data)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:33396/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(data);
var response = await client.PostAsJsonAsync<StringContent>("api/Service/Post", content);//nothing happens after this line.
}
}
Web api controller:
public class MyController : ApiController
{
[HttpPost]
public HttpResponseMessage Post(HttpRequestMessage request)
{
var someText = request.Content.ReadAsStringAsync().Result;
return new HttpResponseMessage() { Content = new StringContent(someText) };
}
}
But here problem is:
var response = await client.PostAsJsonAsync<StringContent>("api/Service/Post", content);
Nothing happens after this line that is operation blocks on this line.
So can anybody guide me with this?
By independently we determined in the SO C# chat room that you really mean "Asynchronously".
Your solution is the code above, saving this data to a WebAPI endpoint so any solution to the problem needs to be in 2 parts ...
PART 1: The Client Part
On the client all we need to do is make the call asynchronously in order to free up the current thread to carry on receiving data on the incoming serial port, we can do that like so ...
// build the api client, you may want to keep this in a higher scope to avoid recreating on each message
var api = new HttpClient();
api.BaseAddress = new Uri(someConfigVariable);
// asynchronously make the call and handle the result
api.PostAsJsonAsync("api/My", str)
.ContinueWith(t => HandleResponseAsync(t.Result))
.Unwrap();
...
PART 2: The Server Part
Since you have web api i'm also going to assume you are using EF too, the common and "clean" way to do this, with all the extras stripped out (like model validation / error handling) might look something like this ...
// in your EF code you will have something like this ...
Public async Task<User> SaveUser(User userModel)
{
try
{
var newUser = await context.Users.AddAsync(userModel);
context.SavechangesAsync();
return newUser;
}
catch(Exception ex) {}
}
// and in your WebAPI controller something like this ...
HttpPost]
public async Task<HttpResponseMessage> Post(User newUser)
{
return Ok(await SaveUser(newUser));
}
...
Disclaimer:
The concepts involved here go much deeper and as I hinted above, much has been left out here like validation, error checking, ect but this is the core to getting your serial port data in to a database using the technologies I believe you are using.
Key things to read up on for anyone wanting to achieve this kind of thing might include: Tasks, Event Handling, WebAPI, EF, Async operations, streaming.
From what you describe it seems like you might want to have a setup like this:
1) your windows form listens for serial port
2) when new stuff comes to port your windows forms app saves it to some kind of a queue (msmq, for example)
3) you should have separate windows service that checks queue and as it finds new messages in a queue it sends request to web api
Best solution for this problem is to use ConcurrentQueue.
Just do search on google and you will get planty of samples.
ConcurrentQueue is thread safe and it support writing and reading from multiple threads.
So the component listening to the searal port can write data to the queue. And you can have 2 or more tasks running parallel which listening to this queue and update db as soon as it receives data.
Not sure if it's the problem, but you shouldn't block on async code. You are doing RunAsync(str).Wait(); and I believe that's the problem. Have a look at this blog post by Stephen Cleary:
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

Using Web API for a Windows Service to Receive Commands and Perform Tasks via Polling?

I have a project where I need to create a windows service that, when instructed via a command, will perform various tasks. This server would run on multiple servers and would effectively perform the same kind of tasks when requested.
For example, I would like to have a Web API service that listens for requests from the servers.
The service running on the server would send a query to Web API every 25 secs or so and pass to it its SERVERNAME. The Web API logic will then look up the SERVERNAME and look for any status updates for various tasks... I.E., if a status for a DELETE command is a 1, the service would delete the folder containing log files... if a status for a ZIP command is a 1, the service would zip the folder containing log files and FTP them to a centralized location.
This concept seems simple enough, and I think I need a nudge to tell me if this sounds like a good design. I'm thinking of using .NET 4.5 for the Windows Service, so that I can use the HttpClient object and, of course, .NET 4.5 for the Web API/MVC project.
Can someone please get me started on what a basic Web API woudld look like provide status updates to the Windows services that are running and issue commands to them...
I'm thinking of having a simple MVC website that folks will have a list of servers (maybe based on a simple XML file or something) that they can click various radio buttons to turn on "DELETE", "ZIP" or whatever, to trigger the task on the service.
I do something similar. I have a main Web API (a Windows Service) that drives my application and has a resource called /Heartbeat.
I also have a second Windows Service that has a timer fire every 30 seconds. Each time the timer fires it calls POST /heartbeat. When the heartbeat request is handled, it goes looking for tasks that have been scheduled.
The advantage of this approach is that the service makes the hearbeat request is extremely simple and never has to be updated. All the logic relating to what happens on a heartbeat is in the main service.
The guts of the service are this. It's old code so it is still using HttpWebRequest instead of HttpClient, but that's trivial to change.
public partial class HeartbeatService : ServiceBase {
readonly Timer _Timer = new System.Timers.Timer();
private string _heartbeatTarget;
public HeartbeatService() {
Trace.TraceInformation("Initializing Heartbeat Service");
InitializeComponent();
this.ServiceName = "TavisHeartbeat";
}
protected override void OnStart(string[] args) {
Trace.TraceInformation("Starting...");
_Timer.Enabled = true;
_Timer.Interval = Properties.Settings.Default.IntervalMinutes * 1000 * 60;
_Timer.Elapsed += new ElapsedEventHandler(_Timer_Elapsed);
_heartbeatTarget = Properties.Settings.Default.TargetUrl;
}
protected override void OnStop() {
_Timer.Enabled = false;
}
private void _Timer_Elapsed(object sender, ElapsedEventArgs e) {
Trace.TraceInformation("Heartbeat event triggered");
try {
var httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(_heartbeatTarget);
httpWebRequest.ContentLength = 0;
httpWebRequest.Method = "POST";
var response = (HttpWebResponse)httpWebRequest.GetResponse();
Trace.TraceInformation("Http Response : " + response.StatusCode + " " + response.StatusDescription);
} catch (Exception ex) {
string errorMessage = ex.Message;
while (ex.InnerException != null) {
errorMessage = errorMessage + Environment.NewLine + ex.InnerException.Message;
ex = ex.InnerException;
}
Trace.TraceError(errorMessage);
}
}
}
You can do it with ServiceController.ExecuteCommand() method from .NET.
With the method you can sand custom command to windows' service.
Then in your service you need to implement ServiceBase.OnCustomCommand() to serve incomming custom command event in service.
const int SmartRestart = 8;
...
//APPLICATION TO SEND COMMAND
service.ExecuteCommand(SmartRestart);
...
//SERVICE
protected override void OnCustomCommand(int command)
{
if (command == SmartRestart)
{
// ...
}
}

SignalR notification system

This is my first time playing around with SignalR. I am trying to build a notification system where the server checks at regular intervals to see if there is something (query database) to broadcast and if there is then it broadcasts it to all the clients.
I came across this post on Stackoverflow and was wondering if modifying the code to make a DB call at a particular interval was indeed the right way to do it. If not is there a better way to do it?
I did see a lot of Notification related questions posted here but none with any code in it. Hence this post.
This is the exact code that I am using:
public class NotificationHub : Hub
{
public void Start()
{
Thread thread = new Thread(Notify);
thread.Start();
}
public void Notify()
{
List<CDCNotification> notifications = new List<CDCNotification>();
while (true)
{
notifications.Clear();
notifications.Add(new CDCNotification()
{
Server = "Server A", Application = "Some App",
Message = "This is a long ass message and amesaadfasd asdf message",
ImgURL = "../Content/Images/accept-icon.png"
});
Clients.shownotification(notifications);
Thread.Sleep(20000);
}
}
}
I am already seeing some weird behaviour where the notifications come more often than they are supposed to. Even though I am supposed to get it every 20s I get it around 4-5 secs and I get multiple messages.
Here is my client:
var notifier = $.connection.notificationHub;
notifier.shownotification = function (data) {
$.each(data, function (i, sample) {
var output = Mustache.render("<img class='pull-left' src='{{ImgURL}}'/> <div><strong>{{Application}}</strong></div><em>{{Server}}</em> <p>{{Message}}</p>", sample);
$.sticky(output);
});
};
$.connection.hub.start(function () { notifier.start(); });
Couple of notes:
As soon as a second client connects to your server there will be 2 threads sending the notifications, therefore if you ave more than one client you will have intervals smaller than 20s
Handling thread manually within ASP.NET is considered bad practice, you should avoid this if possible
In general this smells a lot like polling which is kind of the thing SignalR lets you get rid of since you don't need to signal the server/client
In order to solve this you need todo something like this (again, threads in a web application are generally not a good idea):
public class NotificationHub : Hub
{
public static bool initialized = false;
public static object initLock = new object();
public void Start()
{
if(initialized)
return;
lock(initLock)
{
if(initialized)
return;
Thread thread = new Thread(Notify);
thread.Start();
initialized = true;
}
}
public void Notify()
{
List<CDCNotification> notifications = new List<CDCNotification>();
while (true)
{
notifications.Clear();
notifications.Add(new CDCNotification() { Server = "Server A", Application = "Some App", Message = "This is a long ass message and amesaadfasd asdf message", ImgURL = "../Content/Images/accept-icon.png" });
Clients.shownotification(notifications);
Thread.Sleep(20000);
}
}
}
The static initialized flag prevents multiple threads from being created. The locking around it is to ensure that the flag is only set once.
I am working on the same task over here. Instead of continuously checking the database, I created my own events and listener, where an event is RAISED when a NOTIFICATION IS ADDED :) What do you think about that?

Ajax server process with live interface progress status?

I have a process that retrieves html from a remote site and parses it. I pass several URL's into the method, so I would like to ajaxify the process and give a screen notification each time a URL completes parsing. For example, this is what I am trying to do:
List<string> urls = ...//load up with arbitary # of urls
foreach (var url in urls)
{
string html = GetContent(url);
//DO SOMETHING
//COMPLETED.. SEND NOTIFICATION TO SCREEN (HOW DO I DO THIS)
}
public static string GetContent(string url)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "GET";
using (var stream = request.GetResponse().GetResponseStream())
{
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
return reader.ReadToEnd();
}
}
}
In each iteration in the loop, I want to show the URL was completed and moving on to the next one. How can I accomplish this?
The first thing you need to worry about is the fact (I'm assuming) that you're running a potentially long-running operation in ASP.NET code. This will become a problem when you run in to IIS timeouts. (By default, 90 seconds.) Assume you're processing ten URLs, each of which takes 15 seconds to complete reader.ReadToEnd() – your code will time out and get killed after the sixth URL.
You might be thinking "I can just crank up the timeout," but that's not really a good answer; you're still under time pressure.
The way I solve problems like this is to move long-running operations into a stand-alone Windows Service, then use WCF to communicate between ASP.NET code and the Service. The Service can run a thread pool that executes requests to process a group of URLs. (Here's an implementation that allows you to queue work items.)
Now, from your web page, you can poll for status updates via AJAX requests. The handler in your ASP.NET code can use WCF to pull the status information from the Service process.
A way to do this might be to assign each submitted work unit a unique ID and return that ID to the client. The client can then poll for status by sending an AJAX request for the status of work unit n. In the Service, keep a List of work units with their statuses (locking it as appropriate to avoid concurrency problems).
public class WorkUnit {
public int ID { get; set; }
public List<string> URLs { get; set; }
public int Processed { get; set; }
}
private var workUnits = new List<WorkUnit>();
private void ExecuteWorkUnit(int id) {
var unit = GetWorkUnit(id);
foreach (var url in unit.URLs) {
string html = GetContent(url);
// do whatever else...
lock (workUnits) unit.Processed++;
}
}
public WorkUnit GetWorkUnit(int id) {
lock (workUnits) {
// Left as an exercise for the reader
}
}
You'll need to fill in methods to add a work unit, return the status of a given work unit, and deal with the thread pool.
I've used a similar architecture with great success.

Categories