In asp.net I want to give the customer an immediate response and close the connection, and then continue execution which may be lengthy and display unimportant messages. But none of this should be visible to the customer.
I already tried Response.Flush / Close / End, CompleteRequest and anonymous delegates, but couldn't get it to work with any of this.
Sample:
Response.Write("time: "+HttpContext.Current.Session["test"]);
MagicallyEndReponse(); //But how?
Thread.Sleep(10000); //Customer should not experience any delay
HttpContext.Current.Session["test"] = DateTime.Now; //This should be available when reloading 15s later
Response.Write("BORING INFO!"); //Customer should not see this
I wouldn't recommend background thread processing in an ASP.NET application, it's not what ASP.NET or IIS is designed for.
My advice would be look at having a separate service (e.g. an internal Windows Service) which picks up work from the website and processes it, this would allow you to write a more robust multi-threaded application. You could use a durable messaging system like MSMQ / NServiceBus to pass messages to / from the service (this would mean no work is lost if the website happened to go down or restart).
The natural response for these types of request would be 202 Accepted and then possibly exposing an API for the client to query to check on the progress.
Related
I have an ASP.NET Core Web API project. That has one controller with a method called GetLocations
GetLocations connects to 5 other web services on the internet. Gathers some info and return a collection via json. In this method I am caching the data every 5 mins using In Memory caching.
If the cache expires, it tries to connect to all 5 services and get the info and so on.
My problem is:
I have a lot of users requesting this data constantly, 50 requests a second to this API.
When the cache expires I believe there is some kind of thread locking. I have limited visibility into the project at the moment but I suspect that all these requests are calling the method and reaching out to the 5 dependent services until one of them gets a completed response from all 5.
Is my assumption right? If so how can I go about fixing this? Will I need to make each call to the web services async? Will that help this scenario? I am not 100% sure because the requests are what triggers the method call.
You should definitely make the calls to the external services use Async / Await.
That's just a given - as the best practice is to always use async for I/O heavy operations (such as calling a third-party service).
Now, you should also create a class that manages these calls. You can add it as a Singleton in your IoCConfig. In that class, make sure you're "locking" to avoid the issue you just described and not call the underlying services numerous times while the cache is being built.
Check here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/lock-statement
You are facing this issue because of following reason.
You are using Cache and it will expire at some definite time.
After Cache expire you call external web service method to collect the data. Now at this point of time it might happen that other request in queue get choose for execution.
Once that another request is chosen for execution it also end up checking cache and it now data in cache then execute external service and so on for other request.
Solution to this.
First Check cache contains data or not.
If Not create lock so following section only be executed by single thread.
Now in that lock section again check for cache and if cache contains data then simply return but it does not contains then call external service.
At this point of time if another thread get selected for execution then it has to wait for execlsive section to complete its works.
Once that section get completed it store data in cache and so after if any queued or new request is there it choose data from cache.
Note : It should something like this.
public List<string> GetData()
{
if(Cache[key] == null)
{
lock(obj) // obj should be static
{
if(Cache[key] == null)
{
// Load data from service
Cache[key] == data;
}
}
}
return (List<string>)Cache[Key];
}
I'm very new to Web API and I have an unusual pattern that I need to implement. In the Post method of my controller, it is to take an object which includes a CallbackURL. It will then immediately return an HTTP response to the caller. Afterwards, it will use a 3rd party, off-site API to perform some work with the object. Once that work is done, the controller is to post the results of that work to the CallbackURL.
However, I do not know how to implement this in Web API. Once I return the HTTP response, the controller's lifecycle is over, correct? If so, how do I perform the work I need to do after I return the response?
If you only need to post results to a url and not to the client that initiated the call, you could possibly do something as easy as this:
public string MyAPIMethod(object input)
{
Task.Factory.StartNew(() =>
{
//call third-party service and post result to callback url here.
});
return "Success!";
}
The api call will return right away, and the Task you created will continue the processing in a different thread.
Creating a task to finish up the request (as suggested by Jason P above) will most likely solve the problem, thread-safety provided. However that approach might hurt the performance of your Web service if calls to the 3rd party API take a significant amount of time to complete and/or you are expecting many concurrent clients. If that was the case, your problem seems to be the perfect candidate for a service pattern called "Request/Acknowledge/Callback" (also "Request/Acknowledge/Relay"). Using that pattern, your Web API method will just store each request (including the callback URL) into a queue/database and return quickly. A separate module (possibly running on more than one machine, depending on the number and complexity of the tasks) will take care of completing the tasks, and subsequently notifying completion through the callback URL (please see http://servicedesignpatterns.com/ClientServiceInteractions/RequestAcknowledge).
This is presuming you want to return the results of your 3rd-party query to the caller.
You're correct, this is outside of what's possible with WebAPI. Once you return the HTTP Response, the client also has no connection to your server.
You should look into Asp.Net SignalR, which allows a persistent connection between the client and server, working in modern browsers, and even back to IE7 (though officially unsupported), as well as supporting non-browser clients.
You can then do a couple of things, all of which require the client to connect to SignalR first.
Option 1: You can call your WebApi controller, which can return, but not before launching a task. This task can query the 3rd party api, then invoke a function on the caller via SignalR with the results that you want to provide.
Option 2: You can call a SignalR Hub action, which can talk back to your client. You can tell your client the immediate response, query the 3rd-party api, then return the results you want to provide.
I am using RIA services in a Silverlight project. I am using the LoadOperation class on the client side to load some data from the server.
In the process of loading that data the request might be superseded by a newer request for different data. This is based on multiple LoadOperations being made to the server, then the user clicking a cancel button.
If I take my LoadOperation and call the 'Cancel' method on it, the operation seems to cancel, but the server side code is not stopped, and using fiddler I can see that the operation completes and an HTTP status code of 200 is returned.
When you call 'Cancel' what does that do on the server, I would expect it to call a ThreadAbortException or something like that? Can this be improved?
So I had a look at the decompiled RIA Services source and it seems like the cancel is client side only. No change to the server-side process is made.
Basically when you run operation.Cancel(), it makes sure the operation can be canceled (operation.CanCancel), and then marks it as canceled, and triggers the completion action.
This means that the server-side operation still continues, but nothing is done with the response client side when it completes
Once the operation has completed, you'll need to check the operation.IsCanceled property to see if that operation was canceled. If so, just ignore the result.
From what I understand cancel of serverside execution using the loadoperation is not available.
you could tough run your own cancel impelmentation:
(depending on if you are using DomainService base or LinqToEntitiesDomainService base the impelmentation will variate)
sevice side
in your service method start the load in a new thread
put that thread object in the session
your thread object should be in a way that you can cancel the DbConnection...
perpare a service method (Invoke) to Cancel the currently executing
thread object registered in the session, and remove it from the session
client side
call cancel on the LoadOperation object and invoke the cancel request.
one caveat is that you comment out the OnSessionStart and stop in global.asax in order
to execute ria services in a multithreaded way per user else each request will wait till a previous request is finished (this has to do with ria service threads and not our thread object)
hope this helps
best regards
PS: we also use a similar solution for pessimistic lock with RIA Services and EntityFramework, ...
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.
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.