Ok what id like to do is to load a page that displays 90% of content, and load the last 10% asynchronously.
As im rendering the page i programatically create an update panel which i pass to a thread. When this thread finishes it then updates the updatepanel on the main thread.
public void test(object parameter)
{
Thread.Sleep(2000);
var updpanel = (UpdatePanel)parameter;
updpanel.ContentTemplateContainer.Controls.Add(new LiteralControl("HI"));
updpanel.Update();
}
protected void Page_Load(object sender, EventArgs e)
{
var th = new Thread(new ParameterizedThreadStart(test));
var updpanel = new UpdatePanel() { UpdateMode = UpdatePanelUpdateMode.Conditional };
ContentPlaceHolder1.Controls.Add(updpanel);
th.Start(updpanel);
}
Failing this, in a single threaded approach, do i just keep polling the page to see if it has finished or not?
One thing to keep in mind is, even though ASP.Net development seems like it's closely related to windows development, there's a big difference: your code to handle a request, most of the time, is executed in a fraction of a second and done... In this example, once the main thread has completed, the generated page has already been sent to the requestor, leaving your secondary thread running in the background of your server, working on a page that has already been sent out.
What you'll probably need to do is generate the 90% of the page, and then send it out. On your page, you will need to use ajax to, on page load (for the client), request the other 10% from the server... You'll probably want to do the javascript with a library like jquery, and on the server end setup a web service to handle the requests.
You cannot use threading this way on the server. Your second thread will probably not complete until after the page have been processed and the request lifecycle is complete.
Depending on what you are trying to achieve: do you need to process data in parallel server-side you should look into Asynchronous Pages in ASP.Net 2.0.
Another approach would be to render the page ("90%" as you call it) and use ajax on the client to request additional data.
Related
I have a long task that runs, and can take up to 15 minutes, I allow the user to only run one at a time. This all works well but I can't get my results and have a cancel button.
Currently I have the cancel button write a file, and if that file is present during a loop, the task completes and self deletes. However, I can't get the results of the task to do anything, so errors aren't reported or anything. What is your recommendation on doing this?
Libraries needed for below (iirc)
using System
using System.Threading
using System.Threading.Tasks
Here is a rough example of my execution code.
protected void Execute_Click(object sender, EventArgs e)
{
Task taskExecute = new Task(()=>
{
// 15 minute task
// I also look for cancelation file during loop periodically.
}
taskExecute.Start();
// If i wait for the task, it freezes the webpage so this is commented out
// taskExecute.Wait();
}
here is my cancelation code:
protected void Cancel_Click(object sender, EventArgs e)
{
//code to write file that long script looks for...
}
If you're wondering, the code is called via <asp:button onclick="Execute_Click"> calls. etc...
Also, I can click the cancel button if I refresh the page, but that defeats the purpose, as the responses in my results box won't be visible.
What you're looking for can be accomplished with Azure Durable Functions and add in SignalR/Websockets if you're looking to track state in real-time without polling.
Calls to durable functions return with an ID that you can check on intermittently using a built-in state management endpoint on your function. State objects support custom data as well. This loose coupling allows you to execute and forget calls to the API without blocking any threads. Each call returns a unique ID, so you can run multiple instances in parallel, and easily track them. Connect your web client and web service to a websockets channel (SignalR), and you can send real-time updates or events from your webservice TO your webclient, including a "process completed" event that you can use to trigger the download/render of output/logs/etc...
I have a web application that, based on input from a user, calculates a combination of products to fit the requirements and returns a quote for the user. This calculation can take some time, so I am displaying a progress dialog to the user.
The progress dialog is a UserControl with an UpdatePanel, that contains some text labels, a button, and a timer which is used for the AsynchPostBackTrigger trigger. This is declared on my aspx page but is hidden by default.
Before, I created a separate thread to do the heavy work while updating the progress dialog using the main thread. There were no issues there. here is where I start the thread:
protected void StartQuoteCalculation(object sender, EventArgs e)
{
TotalPanelCount = CurrentQuote.PanelCount;
ProgressDialog.ShowPopup();
CalculateQuoteThread = new Thread(new ThreadStart(CalculateQuote));
CalculateQuoteThread.Start();
}
Where CalculateQuote does the heavy work.
However, I've recently had to change something unrelated to this exactly, but it is now affecting the calculation of the combinations.
A variable that's used during the quote calculation is now stored in the user Session. The session is lost when we start the worker thread above so when accessing this variable it was throwing a NullReference exception. To fix this, I've tried passing the HttpContext.Current to the new thread like so:
protected void StartQuoteCalculation(object sender, EventArgs e)
{
TotalPanelCount = CurrentQuote.PanelCount;
ProgressDialog.ShowPopup();
HttpContext ctx = HttpContext.Current;
CalculateQuoteThread = new Thread(new ThreadStart(() =>
{
HttpContext.Current = ctx;
CalculateQuote();
}));
CalculateQuoteThread.Start();
}
However, the session seems to stay valid for a short while, but after a few seconds it is still throwing NullReferenceException. HttpContext.Current is valid, but the HttpContext.Current.Session is null.
So, I have a few questions:
If it's possible to pass the HttpContext to the other thread, why is
the Session going to null after short time? How can I prevent this?
Is it possible to create, display and update the ProgressDialog panel in the new thread? (to keep the product calculation in the main thread with valid Session data)
I've tried a couple things on #2, but as I am not so knowledgable on asp.net and web development in general, I haven't been successful.
Rather than storing individual variables in session I would store the entire object which contains the state of that calculation. That way as the website is polling for status updates the application can retrieve the current state by retrieving that object from session. It's going to be "in progress," "complete" (the output), or an exception (never happens!)
If you want users to be able to run multiple processes then you could store a Dictionary<guid, YourProcess> and then the client could receive a guid and use it to request the status.
This way the client has a reference to the object which is executing that process, but the thread executing that process doesn't need a reference to the Session or HttpContext. It only needs to know which instance of that class it's operating on, and update it with either progress or the final result.
I have inherited a legacy app and there is some code that can be simplified down to:
protected void SomeButton_Click(object sender, EventArgs e)
{
var otherThread = new System.Threading.Thread(delegate()
{
System.Threading.Thread.Sleep(20000);
FireAnAsyncProcess();
});
otherThread.Start();
thankYouPanel.Visible = true;
otherPanel.Visible = false;
}
The FireAnAsyncProcess() seems to be intermittently failing to run. This could of course be a number of things but I wondered:
Given the pause of 20000 above, will FireAnAsyncProcess() always be called even after the button click has been handled? Does the new thread get called regardless of what happens to the thread that called it?
Edit: the result/outcome of FireAnAsyncProcess() is not required on the web page and the page is not intending to wait for them. Rather it is trying to start the thread and assume it gets fired.
This is web environment - each request is handled in their own thread context, and the Thread.Start() isn't useful: the child thread will be aborted after the request been handled, and the parent thread aborted.
So if you need to run some piece of work on Button_Click, you should host a WCF service or Windows Service or something being able to accept some message from web-application and run the job in background.
I have a form post which should call an action asynchronously.
public async Task<ActionResult> SaveW2Document(Models.DocumentData documentData)
{
some code here
var ID = await Task.Run<int>(() => somObject.SaveDocument(uploadDocument));
//Code that uses ID
if(ID == something){
//Do something
}
}
However it gets called synchronously and my other actions do not execute until this action gets completed.Is it something else that I need to do?
I have a form post which should call an action asynchronously.
I think you're misunderstanding what "asynchronous" means in this context. Using async controller actions (or HTTP request handlers in general) means that the web server can invoke the operations asynchronously and not hold a thread in a wait state while the operation completes. This allows the web server to handle more concurrent requests more effectively.
The browser, however, is waiting for the response. The asynchronous nature of any given operation generally isn't defined by the operation itself, but by whatever invokes it. (For example, note how within this operation there is another asynchronous operation being awaited. If this code didn't await it, the behavior would be very different. But the method being invoked wouldn't change between those two scenarios.)
This makes sense, though. After all, what is the browser going to do if it doesn't wait for the response from the server? Display a blank page?
You can make asynchronous calls to a web server from client-side code by way of using AJAX. That would allow the page to invoke the server-side operation and listen for the response without blocking or changing the context of the page itself. But if the whole page context is posting to the server and waiting to load a new page, it needs to receive that new page before it can display it.
Well I might not have been able to ask the question correctly.But I found the solution to the problem that was occuring. MVC has a behaviour that blocks concurrent requests coming from the same session.I will either have to Disable Session or make it readonly for that controller. Refer this link.
So adding [SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)] to my controller helped.
Here is how I am trying to do:
void Fetch()
{
//Code To Fetch Data
}
protected void btn_Click(object sender, EventArgs e)
{
lblSatus.Text = "Fetching Data...";
Thread t = new Thread(Fetch);
t.Start();
t.Join();
lblSatus.Text = "Got Data";
}
But on click of button it directly display GotData.
Any idea what I am doing wrong here.
Thanks
After the t.Start(); you need something that does a check ...along the lines of:
while (!t.Completed) // pseudo
{
// wait
Thread.Sleep(10);
}
Otherwise, the thread spins up and begins execution while the Main thread simply falls through.
I think you're misunderstanding the relationship between threads on the server-side when processing an ASP.NET request, and the experience on the browser-side. Specifically, for a given HttpRequest (in your example, the Request is a postback, which triggers the btn_Click event), there will be one and only one Response. This is true, regardless of what thread-manipulation you're doing on the server-side.
In your example, it looks like you're expecting the server to respond to the browser, with lblSatus's value as "Fetching data...". Then a little later when the operation has completed, the server would "respond" to the browser again, with lblSatus's value as "Got Data". This will not work, due to the "handshake" nature of Http communication: one request will result in one response (and thus, only one value for lblSatus).
The thing you're trying to do (give the end user a logical "progress bar" during a long-running operation) is a common requirement. Unfortunately, I know of no easy built-in way to do this in ASP.NET. I suspect most people do it via a polling model: the browser initiates the action, the server kicks it off in some sort of asynchronous fashion that does NOT delay the response being sent back to the browser. Then the browser periodically (via javascript, most likely) reaches back to the server to get an update on the operation's progress.