Display output from command line program realtime in asp.net - asp.net

I am writing a web app where the application runs a command on the system using System.Diagnostics class.
I wanted to display realtime output from a command which takes a lot of time to complete. After searching a bit, I found that BeginOutputReadLine can stream output to an event handler.
I am also using jquery ajax to call this method and have the process run asynchronously.
So far, I am trying to do it this way:
Process p2= new Process();
p2.OutputDataReceived += new DataReceivedEventHandler(opHandler);
p2= Process.Start (psi2);
p2.BeginOutputReadLine();
I have declared a class with a static variable to store the output of the command as a Label on the page wont be accessible from a static method.
public class ProcessOutput
{
public static string strOutput;
[WebMethod]
public static string getOutput()
{
return strOutput;
}
}
In the event handler for BeginOutputReadLine, set the variable with the line from output.
private static void opHandler(object sendingProcess,DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
ProcessOutput.strOutput= outLine.Data;
}
}
and from the aspx page, I am calling the method to get the value of strOutput
$(document).ready(function() {
setInterval(function() {
$.ajax({
type: "GET",
url: "newscan.aspx/getOutput",
data: "",
success: function(msg){
$('#txtAsyncOp').append(msg.d);
}
});
}, 1000);
});
I dont know why, but the lable is not getting updated. If I put alert, I get 'undefined' in the alert box every 10 seconds.
Can anybody suggest me how to do it correctly?

Each request begins a new thread as a part of the Request pipeline. This is by design. Each thread has its own stack and can't access each others stacks. When a thread starts running a new method it stores the arguments and local variables in that method on its own stack. Long story short you won't be able to assign that variable and expect to retrieve its value from another Request.
There are a couple approaches you can take, you can scope it to the session variable (most common) with:
System.Web.HttpContext.Current.Session["variable"] = value ;
Or you set it to application scope using:
if (System.Web.Caching.Cache["Key1"] == null)
System.Web.Caching.Cache.Add("Key1", "Value 1", null, DateTime.Now.AddSeconds(60), Cache.NoSlidingExpiration, CacheItemPriority.High, onRemove);
Alternatively, you can log the output to a database or file and echo out the results via the WebMethod. If your long running process is running asynchronously, you won't have access to the HttpContext -- so the Session state bag will not be available; the application Cache could be used, however it is generally not used for this type of mechanism (cache is available for performance reasons, not a persistence mechanism -- its important to remember that you cannot control when your web application recycles).
I'd highly suggest writing to a database or log file. Asynchronous processes commonly require logged output or trace to diagnose potential problems and to validate results.
Furthermore, because you cannot control when the web app recycles, you can easily lose control of that child process you're launching. A better design would start an asynchronous method in-process, or an out-of-process application or service that polls a database to pick up jobs (possibly use the task scheduler or cron depending on your platform).

Related

.NET Generic Host - Is it possible to stop and restart a host?

Consider this extremely simple .NET Core 3.1 (and .NET 5) application with no special config or hosted services:
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
internal class Program
{
public static async Task Main(string[] args)
{
var builder = Host.CreateDefaultBuilder(args);
builder.UseWindowsService();
var host = builder.Build();
var fireAndforget = Task.Run(async () => await host.RunAsync());
await Task.Delay(5000);
await host.StopAsync();
await Task.Delay(5000);
await host.RunAsync();
}
The first Run (sent as a background fire and forget task only for the purpose of this test) and Stop complete successfully. Upon calling Run a second time, I receive this exception:
System.AggregateException : 'Object name: 'EventLogInternal'.Cannot access a disposed object. Object name: 'EventLogInternal'.)'
If I do the same but using StartAsync instead of RunAsync (this time no need for a fireAndForget), I receive a System.OperationCanceledException upon called StartAsync the second time.
Am I right to deduce that .NET Generic Host aren't meant to be stopped and restarted?
Why do I need this?
My goal is to have a single application running as a Windows Service that would host two different .NET Generic Host. This is based on recommendation from here in order to have separate configuration and dependency injection rules and message queues.
One would stay active for all application lifetime (until the service is stopped in the Windows services) and would serve as a entry point to receive message events that would start/stop the other one which would be the main processing host with full services. This way the main services could be in "idle" state until they receive a message triggering their process, and another message could return them to idle state.
The host returned by CreateDefaultBuilder(...).Build() is meant to represent the whole application. From docs:
The main reason for including all of the app's interdependent resources in one object is lifetime management: control over app startup and graceful shutdown.
The default builder registers many services in singleton scope and when the host is stopped all of these services are disposed or switched to some "stopped" state. For example before calling StopAsync you can resolve IHostApplicationLifetime:
var appLifetime = host.Services.GetService<IHostApplicationLifetime>();
It has cancellation tokens representing application states. When you call StartAsync or RunAsync after stopping, all tokens still have IsCancellationRequested set to true. That's why the OperactionCancelledException is thrown in Host.StartAsync.
You can list other services during configuration:
For me it sounds like you just need some background jobs to process messages but I've never used NServiceBus so I don't know how it will work with something like Hangfire. You can also implement IHostedService and use it in the generic host builder.
I'm doing something like:
do
{
using IHost host = BuildHost();
await host.RunAsync();
} while (MainService.Restart);
with MainService constructor:
public MainService(IHostApplicationLifetime HostApplicationLifetime)
MainService.Restart is a static bool set by the MainService itself in response to some event which also calls HostApplicationLifetime.StopApplication().

Azure web job singleton function is locked

I am using Azure web job to run some logic continuously. The function is a singleton function. However, I am getting "Waiting for lock" message after I tried to run this function after a restart of the web app. Does it mean that another instance of the same function is keeping the lock? How can I resolve this?
The function:
namespace Ns
{
public class Functions
{
[Singleton]
[NoAutomaticTriggerAttribute]
public static async Task ProcessMethod()
{
while(true){
//process logic here
await Task.Delay(TimeSpan.FromSeconds(20));}
}
}
}
The main program:
namespace ns
{
class Program
{
static void Main()
{
var host = new JobHost();
host.RunAndBlock();
}
}
}
The message that I got:
According to the Singleton attribute description the lock is adquired during function execution by a Blob lease.
If another function instance is triggered while this function is
running it will wait for the lock, periodically polling for it.
If you have more than one instance of your App Service Plan, this means that there are more than one Webjob and thus the Dashboard might be showing the locked status of the other Webjobs while one is running.
You can view the blob lease locks that are created on your storage account.
Another option is to try Listener Singletons but I never tried it with Manual triggers.
I disabled the production function in Azure and set the listenerlockPeriod to 15 seconds as described above.
This lessened the locking behavior significantly.

Application_Start timeout?

I have one piece of code that gets run on Application_Start for seeding demo data into my database, but I'm getting an exception saying:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection
While trying to enumerate one of my entities DB.ENTITY.SELECT(x => x.Id == value);
I've checked my code and I'm not disposing my context before my operation, Below is an outline of my current implementation:
protected void Application_Start()
{
SeedDemoData();
}
public static void SeedDemoData()
{
using(var context = new DBContext())
{
// my code is run here.
}
}
So I was wondering if Application_Start is timing out and forcing my db context to close its connection before it completes.
Note: I know the code because I'm using it on a different place and it is unit tested and over there it works without any issues.
Any ideas of what could be the issue here? or what I'm missing?
After a few hours investigating the issue I found that it is being caused by the data context having pending changes on a different thread. Our current implementation for database upgrades/migrations runs on a parallel thread to our App_Start method so I noticed that the entity I'm trying enumerate is being altered at the same time, even that they are being run on different data contexts EF is noticing that something is wrong while accessing the entity and returning an incorrect error message saying that the datacontext is disposed while the actual exception is that the entity state is modified but not saved.
The actual solution for my issue was to move all the seed data functions to the database upgrades/migrations scripts so that the entities are only modified on one place at the time.

ASP.Net Web Service: Running code Asynchronously

I have a web service that can be broken down into two main sections:
[WebMethod]
MyServiceCall()
{
//Do stuff the client cares about
//Do stuff I care about
}
What I'd like to do is run that 2nd part on another thread, so that the client isn't waiting on it: once the user's logic has completed, send them their information immediately, but continue processing the stuff I care about (logging, etc).
From a web service, what is the recommended way of running that 2nd piece asynchronously, to get the user back their information as quickly as possible? BackgroundWorker? QueueUserWorkItem?
You may want to look into Tasks which are new to .NET 4.0.
It lets you kick off an asynchronous operation, but also gives you an easy way to see if it's done or not later.
var task = Task.Factory.StartNew(() => DoSomeWork());
It'll kick off DoSomeWork() and continue without waiting so you can continue doing your other processing. When you get to the point where you don't want to process anymore until your asynchronous task has finished, you can call:
task.Wait();
Which will wait there until the task has completed. If you want to get a result back from the task, you can do this:
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(3000);
return "dummy value";
});
Console.WriteLine(task.Result);
A call to task.Result blocks until the result is available.
Here's a tutorial that covers Tasks in greater detail: http://www.codethinked.com/net-40-and-systemthreadingtasks
The easiest way to fire off a new thread is probably:
new Thread(() =>
{
/// do whatever you want here
}).Start();
Be careful, though - if the service is hit very frequently, you could wind up creating a lot of threads that block each other.

Stoping web service in flex?

is it possible to stop a web service from executing?
I have a flex web application that searches clients with both full name and client id, when searching by name sometimes the usuer just types the last name and it takes a long time.
Since the app is used when clients are waiting in line, I would like to be able to stop the search and use their full name or id instead, and avoid waiting for the results and then having to search the user manually within the results.
thanks
edit: Sorry, I didn't explain myself correctly, when I meant "web service" I actually meant mx.rpc.soap.mxml.WebService, I want to stop it from waiting for the result event and the fault event. thanks.
There is actually a cancel(..) method explicitly for this purpose, though it is a little burried. Using the cancel method will cause the result and fault handlers not to be called and will also remove the busy cursor etc.
Depending on how you run your searches (ie. separate worker process etc), it is also possible to extend this by added in a cancelSearch() web service method to kill these worker processes and free up server resources etc.
private var _searchToken:AsyncToken;
public function doSearch(query:String):void
{
_searchToken = this.searchService.doSearch(query);
}
protected function doSearch_resultHandler(event:ResultEvent):void
{
trace("doSearch result");
trace("TODO: Do stuff with results");
_searchToken = null;
}
protected function doSearch_faultHandler(event:FaultEvent):void
{
trace("doSearch fault: " + event.fault);
_searchToken = null;
}
public function cancelSearch():void
{
var searchMessageId:String = _searchToken.message.messageId;
// Cancels the last service invocation or an invokation with the
// specified ID. Even though the network operation may still
// continue, no result or fault event is dispatched.
searchService.getOperation("doSearch").cancel(searchMessageId);
_searchToken = null;
trace("The search was cancelled, result/fault handlers not called");
// TODO: If your web service search method is using worker processes
// to do a search and is likely to continue processing for some time,
// you may want to implement a 'cancel()' method on the web service
// to stop any search threads that may be running.
}
Update
You could use disconnect() to remove any pending request responders, but it also disconnects the service's connection. Then call initialize().
/Update
You cannot stop the web service from executing, because that's beyond the Flex app's control, but you can limit the processing of the web service's response. For instance on the app, have a button like Cancel Search which sets a boolean bSearchCanceled to true.
The result handler for the web service call checks bSearchCanceled; if true just return.

Resources