Moving a time taking process away from my asp.net application - c#

My Asp.net application generates a dynamic pdf. Sometimes this takes a while and is a quite heavy process. Actually i dont want my users to wait for the pdf, just send it to there mail after it generated.
So I tried a webservice. I'm passing an id (to get the data from the database) and some strings to the websercice's method.
But also with a webservice (even with asynchronous calls) the client only receives its response after the pdf is generated. So the user still has to wait.
So I'm kinda stuck, there must be a way i'm overlooking.

You don't need a webservice in order to get the ability to make asynchronous invocations.
You can just use ThreadPool.QueueUserWorkItem() as a fire-and-forget approach in the ASPX page, then return a reply with some sort of "work item id" - like a receipt or an order number.
Generate the PDF in the WaitCallback you pass to QUWI.
when the pdf is ready, that WaitCallback can send an email, or whatever.
Use a webservice if you want the function to be accessible, outside the webpage. Don't use it strictly for asynchrony.

Issue is that in your ASP.NET page code, you must be invoking the web service synchronously so the page waits till web service returns. You should try invoking the web service asynchronously (or on the different thread) and then don't wait for it to complete. Typically, visual studio generated proxy already has asynchronous overloads that you may use.
Alternately, you may modify your web service code - essentially, when request to your web method comes, you can start PDF generating on a different thread so that your web method may end indicating your client (page in this case) that request has been successfully scheduled for processing.

there are two ways which i know
First ways;
In asp.net code behind (in xxx.aspx.cs file) you can define a void method then you can call the method by starting a thread like below.
protected void SenMail(object prms)
{
int id = int.Parse(prms.ToString());
//mail sending proces
}
//starting SendMail method asynchronous
Thread trd = new Thread(SenMail);
trd.Start(idValue);
Second way;
You can create and mail sender page like "SendMail.aspx", then you can make an ajax request in javascript and no need to wait any response. you can pass id value to aspx page as request parameter.

Related

Not able to update UI after background task completion in my ASP.NET application

I am running a long running function as background task in my asp.net application. Before the task completes the main thread exits (I want it that way only, since if I use the await keyword and make main thread wait till background task completes, I get proxy error with message
Proxy Error
The proxy server received an invalid response from an upstream server since the background task is too long
But once after the task completion neither I am able to refresh the page by redirecting to the same page or neither I am able to override the UI. Is there any way to update UI after main thread completes execution.
My code goes like this:
protected void btnImport_Click(object sender, EventArgs e)
{
var task = ImportThread();
if (task.IsCompleted)
{
DisplaySuccess("Import success");
}
else
DisplayError("Import failed");
}
private async Task<bool> ImportThread()
{
try
{
var success = await Task<bool>.Run(() => new Manager().Import().ConfigureAwait(false);
if (task.IsCompleted)
{
DisplaySuccess("Import success");
}
else
{
DisplayError("Import failed");
}
return true;
}
The above async task awaits the method below which is present in another class.
public bool Import()
{
// some operations here
return true;
}
After this method completes the control return backs to ImportThread() but the code written there to override UI is not updating UI. I need to update UI with the status of import. And also from ImportThread control is not going back to button click event method too.
Please help me with any way to update UI the status of import.
Note: I tried using Redirect.Response in ImportThread() to refresh the page, but that didn't work
Your problem is that you have to grasp and under stand the web page lifecycle here.
You have this case in which the web page is sitting on the users desktop:
Now say the user clicks a button.
You now have this:
var task = ImportThread();
if (task.IsCompleted)
Ok, so the web page is up on the server. You can put in even async awaits until the cows come home, but you STILL HAVE THIS:
So as long as your code runs, or waits, the web page is STILL STUCK UP on the server side. ONLY until code completes and exits does the page travel down to the client side.
AGAIN:
Your code behind cannot halt, and cannot wait for something to finish, since if it does, then the page REMAINS up on the server until processing finished.
THEN AND ONLY THEN does the web page make the trip back down to the client side. This then occurs;
And then the SERVER SIDE PAGE IS TOSSED OUT of memory, and all class variables are DESTROYED!!! The web server is now waiting for ANY USER to post back a page for processing!!
So, if you need to run some kind of long running process?
You have a few choices:
post the page, code behind runs, code behind starts a NEW thread, web page makes trip back to client side. At that point, you need a timer + some type of web method call (ajax) to poll or ask the server if the long running process is done. And since a ajax call does NOT have use of any web controls on that page, or page class variables (remember, AFTER the web page travels down back to client side, the web page is NOT NOT EXISTING web server side in memory, nor is ANY of the class variables existing). So, again, this quite much means some kind of timer, or as noted, a timer + code to call some ajax method. and that long running process will have to VERY likely use session() since you don't have use of controls, or even ViewState.
And you don't necessary have to use a ajax call. You could use a simple JavaScript client side routine with a timer that say clicks a button every 1 or 2 seconds, the code behind runs, and it would then have to get the status of that long running process (again probably from session), and then update the display. And then you could also include code to stop the timer when the status has changed to "done" or whatever.
So code behind does not and will not "update" the web page multiple times. You have ONE round trip, and the code behind must run fast, must finish running, and can't even use a AWAIT command, since then the page will STLL wait, and STILL be stuck up on the server.
If you want to go beyond the simple timer trick approach - which I often use?
Then you need to adopt and introduce into your web site something designed for this type of case -
Thankfully, there is signalR for this purpose, and that no doubt the best option and approach for you, since it is designed for exactly your question and scenario.
SignalR
https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/introduction-to-signalr#:~:text=What%20is%20SignalR%3F%20ASP.NET%20SignalR%20is%20a%20library,process%20of%20adding%20real-time%20web%20functionality%20to%20applications.
If you want to asynchronously notify a user (of completion or failure of anything such as a task), you can use web push notification (using firebase messaging cloud) or SignalR sockets. When you use a background task you lose the main thread and unfortunately, there is no way to respond to the related user.

ASP.NET Web Api, Database connection in Threads

I have an issue with using a Database in a thread in my asp.net Application.
When I want to start my application I want to start a thread called "BackgroundWorker" with it, which runs in the background till the whole application is stopped.
The problem is that I have massive problems with the dbContext in the thread.
I I try to start the walker in my Startup.cs in the methods "ConfigureServices" or "Configure" and then initialize the dbContext in the constructor in the Walker like this "dbContext = new ApplicationContext()" it tells me that the connection is not configured, when I try to operate in the while(true) queue on the database.
If I write an own Controller for the Walker which receives a ApplicationContext in his constructor and then starts a Thread like this, if i call this controller once with a GET Request:
public BackgroundWorker(ChronicusContext dbContext)
{
_dbContext = dbContext;
_messageService = new MailMessageService();
}
// GET: api/backgroundworker
[HttpGet]
[Route("start")]
public void StartWorker()
{
//Thread thread = new Thread(this.DoBackGroundWork);
Thread thread = new Thread(() => DoBackGroundWork(this._dbContext));
thread.Start();
}
public void DoBackGroundWork(ChronicusContext _dbContext)
{
while (true)
{
if (_dbContext.PollModels.Any()) //Here is the exception
{
...
}
}
}
Then I receive an System.ObjectDisposedException that the object is already disposed inside the while (true) queue.
I tried those and similar things in many different ways but allways receive exceptions like these two or that the database connection is closed.
Can somebody help me and tell me, how this works?
Thank you!
Generally, server side multithreading for Web Applications does not happen often and is, most times, a huge no no.
Conceptually, your server is "multithreaded", it handles many HTTP requests from clients/users/other servers. For mobile and web architecture/design, your server(s) process multiple requests and your clients are handling asynchronous calls and dealing with waiting for responses from long running calls like your API method StartWorker.
Think of this scenario, you make a request to your WebAPI method StartWorker, the client, making the request is waiting for a response, putting the work on another thread does nothing as the client is still waiting for a response.
For example, let's consider your client an HTML web page with an Ajax call. You call StartWorker via Ajax, you will be loading data into a HTML table. You will desire, from a UX perspective, to put up a progress spinner while that long running StartWorker responds to your HTML Page Ajax call request. When StartWorker responds, the Ajax call loads the HTML table with the StartWorker response. StartWorker has to respond with the data. If StartWorker responds beforehand than you will have to send a push notification, via SignalR, for example, when the other thread completes and has the data you need for the HTML table.
Hopefully, you see, the call to the WebAPI method, takes the same amount of time from a Ajax request/response perspective, so multithreading becomes pointless in this scenario, a most common web application scenario.
You can have your client UI load other UI elements, showing a progress spinner in HTML table UI area, until your database call is complete and responds with the data to your Ajax call. This way your users know things are happening and something is still loading.
If you still need your additional thread in your API for your project needs, I believe you have to be using Entity Framework 6 or greater to support asynchronous queries, see this tutorial:
http://www.codeproject.com/Tips/805923/Asynchronous-programming-in-Web-API-ASP-NET-MVC
UPDATE
Now that I know you need to run a SQL query on a repeating frequency of time, and you have an Azure Web App, what you want to use is Azure Automation if you are using Sql Azure or create a Sql Server Job if you are using a Sql Server instance as your backend
DbContext is not thread safe. You need to create a new context from inside your thread.
public void DoBackGroundWork()
{
ChronicusContext anotherContext= new ChronicusContext();
while (true)
{
if (anotherContext.PollModels.Any())
{
...
}
}
}

ASP.NET WebAPI Ajax with progress

I have a really long webAPI request that basically does the follow :
1. retrieves a list of item categories from the db
2. for each category, retrieve all the items in the category
Now, the entire process takes a very long time and I don't want the user to wait till the entire process is over, if a category has finished loading I want it to return to the client
Does anyone know how I can do that? Send a request and get progress notifications by the server whenever a part of the request has finished?
You could use SignalR to send the data from the server to the client when it's available.
The other option is polling from the client. The client makes the initial request, which triggers a server side process that prepares the data and keeps it somewhere (in memory, in a database). Then the client polls the server for new available data until the server process finishes.
you need to break your request. use for loop. if elements from first category are downloaded then do something with them before going for second category.
so your request will go inside some loop. You can use Jquery or page methods if you are using asp.net webforms
PushStreamContent might help you:
http://weblogs.asp.net/andresv/asynchronous-streaming-in-asp-net-webapi

Web Services. Get input data, process it at background thread

I've got several web-services: asmx,wcf. At couple of them there are some methods, which take a lot of time for processing, but size of input data for these methods are small and it takes not much time to transfer on the wire. I want move to not sync model. Client passes data to service, service answers that data transfer was correct and process it at background thread witout connection with client. So agter transfering connection should be closed. IS it possible? Can u help me with articles or may be just google request.
John is right - Once you close an http connection, it is done. You can't get back to the same process.
So if you can use another technology that allows duplex on one connection (e.g. WCF), do it!
However,
if you have no choice but to use webservices,
here are three ways to make it work. You may get timeouts on any of them.
Option 1:
Forget the part about 'client answers data was correct.' Just have each thread make its request and wait for the data.
Option 2:
Now, assuming that won't work and you must do the validation, this way requires the client to make 2 requests.
First request: returns valid/invalid.
Second request: returns the long-running results.
Variation of option 2:
If you have timeout problems, you could have the first request generate a GUID or unique database key and start another process, passing it this key, and return the key to the client. (if you can get the server to allow you to start a process - depends on security settings/needs - if not you may be able to start an async thread and have it keep running after the websvc one ends?) The process will do the long task, update the row in the database w/ the unique id when finished, revealing the results plus a 'done' flag. The second request by the client could always return immediately and if the processing is not done, return that, if it is, return the results. The client will repeat this every 5 sec or so until done.
Hacks, I know, but we don't always have a choice for the technology we use.
Don't do this with ASMX web services. They weren't designed for that. If you must do it with ASMX, then have the ASMX pass the data off to a Windows Service that will do the actual work, in the background.
This is more practical with WCF.
We have been writing stuff to interact with the UK gov website and the way they handle something similar is that you send your request and data to the server and it responds saying, roughly, "thanks very much - we're processing it now, please call back later using this id" - all in an XML message. You then, at some point later, send a new http request to the service saying, essentially, "I'm enquiring about the status of this particular request id" and the server returns a result that says either it has processed OK, or processed with errors, or is still processing, please try again in xx seconds.
Similar to option 2 described previously.
It's a polling solution rather than a callback or 2 way conversation but it seems to work.
The server will need to keep, or have access to, some form of persistent table or log for each request state - it can contain eg, the id, the original request, current stage through the workflow, any error messages so far, the result (if any) etc. And the web service should probably have passed the bulk of the request off to a separate Windows service as already mentioned.

How to set Async Page Directive Dynamically so Async Methods work

I am writing some Utility code to send off emails Async.
var mailClient = new SmtpClient(smtpHost);
mailClient.SendCompleted += new SendCompletedEventHandler(mailClient_SendCompleted);
using (var mailMessage = new MailMessage())
{
if (!((System.Web.UI.Page)HttpContext.Current.CurrentHandler).IsAsync)
{
// set to Async????
}
mailClient.SendAsync(mailMessage, new { EmailID });
}
But I get errors because my Pages don't have Async="true" in the page directives.
here is the standard error that you get:
"Asynchronous operations are not allowed in this context. Page starting an
asynchronous operation has to have the Async attribute set to true and an
asynchronous operation can only be started on a page prior to
PreRenderComplete event."
I read this: (last paragraph )
http://msdn.microsoft.com/en-us/magazine/cc163725.aspx
A final point to keep in mind as you
build asynchronous pages is that you
should not launch asynchronous
operations that borrow from the same
thread pool that ASP.NET uses. For
example, calling
ThreadPool.QueueUserWorkItem at a
page's asynchronous point is
counterproductive because that method
draws from the thread pool, resulting
in a net gain of zero threads for
processing requests. By contrast,
calling asynchronous methods built
into the Framework, methods such as
HttpWebRequest.BeginGetResponse and
SqlCommand.BeginExecuteReader, is
generally considered to be safe
because those methods tend to use
completion ports to implement
asynchronous behavior.
Questions:
1) How can I update the page to be Async in my c# code?
2) If I can't what is the down side with forcing all my pages to be Async=true?
3) Is there an even better way to thread my task without being "counterproductive"?
How many different pages do you need to send mail from?
Also, what error did you get when you tried to send async? Please edit your question to contain the entire exception.
Consider creating a single (async) page to send email from. You can call that page by using Server.Transfer, and have it redirect back to your desired page when done.
Finally, if you're sending so many emails that you lose performance when sending mail synchronously, then perhaps you should create a Windows Service to send the actual email. Your ASP.NET page would queue a request to this service (through MSMQ, or WCF) to have the service send the email.

Categories