I need to call a process from my Web API controller action asynchronously so the action doesn't wait for the process to finish to return. How is this done? I'm trying to avoid writing a message queue.
This is an example of what I am looking to do
public JsonResult Index(string key)
{
//call some process here but don't wait for it to finish,
//this would be something like logging or sending an email
// this returns immediately
return new JsonResult
{
...
};
}
Task.Run() or ThreadPool.QueueUserWorkItem()
For logging you might want to write an asynchronous log appender, if using log4net. For email, I would consider using a drop directory and having the SMTP server pick up the mail asynchronously. Both of these would remove the complexity from the controller action and localize it in the component you're using instead.
If you can afford to lose your background work, you can use Task.Run(). If it is important, use QueueBackgroundWorkItem. For more info, take a look at this.
http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx
You could also look at background job schedulers like hangfire or Quartz.net
Below is a nice blog entry on these solutions.
How to run Background Tasks in ASP.Net
Related
I am streaming a file to client for download. But it may happen that file size could be really big (upto few GBs) and thus I don't want to block the user to click other buttons on the webpage which goes to the same controller as Download. From reading on internet, I found that I can make it asynchronous using "Async" and "Completed" suffixes and this is my code:
public void DownloadAsync(string filename, string Id, string docId)
{
AsyncManager.OutstandingOperations.Increment();
// code to get the file from server and send it to client.
AsyncManager.OutstandingOperations.Decrement();
}
public ActionResult DownloadCompleted()
{
return RedirectToAction("Index");
}
public string OtherAction()
{
// code for this action.
}
When I click the Download on webpage and also clicks the "OtherAction" button. It still process the requests synchronously. The "OtherAction" just returns a string to user and is not time intensive and that's why I didn't make it asynchronous.
Do I need to include some code between the .Increment() and .Decrement() operations to wrap the code to download file inside "something" to start a new thread or something like that? I am not able to figure out what other piece I am missing here. I am inheriting the controller from AsyncController.
I think that you are missing some concepts here. You have two parts.
The server
Every request is asynchronous even without using any Async, so the user can send other requests to the server without being blocked.
The client
As long as the user starts the download and don't exit the browser or stop the download, the user can keep doing operations in the same tab or in another. The request that it's being completed by the download don't stop.
So, you don't need to make anything async in the server. I would just recommend my users to use some download manager if the download is several GB heavy
In both contexts, same code, same application , same routes, only controller action differs.
context 1:
synchronous controller - gets called once
public void Index(string parameters)
{
// called only once
}
context 2:
asynchronous controller actions - gets called lots of times
public void IndexAsync(string parameters)
{
// called many times (per image etc on the page)
}
public ActionResult IndexCompleted()
{
return null;
}
my question,
does anyone understand/know why the same routes/actions pick up all the extra requests when I make the controller action asynchronous..?
any help would be very much appreciated,
thanks,
J.
Fire up fiddler and see how the image requests are coming across in both cases. If they come up once for the synchronous method and the browser then realizes it can cache this image or not. Are the URL/Parameters combination the same in the async, sync method?
Please post the responses here on what you see with Fiddler if you can. If you download a demo project at http://msdn.microsoft.com/en-us/library/ee728598.aspx does the same thing happen for you there? Im gathering not which makes me think its a caching issue (off the top of my head).
I know you mention 'same routes' above, but same route and parameters in both cases?
I want to send a bunch of emails, and the obvious way to format them is to render a partial view and send that. The problem is that I'd like to do this in the background, so it isn't immediately obvious how to get access to the methods I need.
Since the job is kicked off by a controller, one thing I was thinking of was something like this:
public ActionResult SendEmails(){
Task.Factory.StartNew(() => DoSendEmails(
// pass in a formatting closure that has access to the
// controller's context
delegate(EmailData) {
return RenderPartialToString("view", EmailData);
}
));
}
Will this work? Is there a better way?
Another option to consider is Postal. You can find a tutorial here to get started, or watch this video from MvcConf. You can send emails asynchronously using the Email.SendAsync() method.
I wrote a project called ActionMailer.Net that allows you to generate emails from your MVC views. Give it a spin and let me know if you like it. I also wrote up some documentation and a nice screencast for the project. Cheers! :)
I would hyper strongly recommend you MvcMailer (note the Send Email Asynchronously section). You may also checkout Scott Hanslemann's blog post about it.
This is the client's request: in some cases there are several forms that don't require authentication or it takes too long for a logged user to finish completing a form and the session expires. In theses cases he wants to retain the data when the user submits the form by serializing and storing it in a SQL table and then, after the user (re)logs in, he is redirected to the respective form and that is repopulated with the data retrieved from the database and deserialized.
The problem is that he wants all the logic of the storing, retrieving and resending the data in the authorization code block. I know how to do the storing, serialization, retrieving, deserialization of data and the user redirection to the respective page, but I don't know hoe to make it generic so that it works for every model on every form.
The client does not want any code for this task done in the form action method. For example:
[HttpPost]
[Authorize]
public ActionResult Create(Post post)
{
if (ModelState.IsValid)
{
post.CreatedBy = (Guid)Membership.GetUser().ProviderUserKey;
post.CreateTime = DateTime.Now;
repo.Add(post);
repo.Save();
return RedirectToAction("Index");
}
else
{
return View(post);
}
}
As you can see he wants to keep it as clean as possible. He had this suggestion:
The server receives a HTTP request in RAW text format. Using this text, it builds the objects (RequestContext, FormCollection collection, etc. etc.). So, you should be able to build in a hook and e.g. save the raw request. After succesfull login, this previous raw text of the HTTP request could be injected in the handling.
I don't really know how to do that or even if it is possible in MVC.
If someone can help I'll be extremely grateful.
Thanks,
ABTeam
The proper way to do this is to capture the user's progress in the database, and provide a mechanism for returning them to the next step in the process. This can be done with a ?step=n parameter in the URL. If the user gets logged off, they can log back in and be returned to the correct step in the process.
Your client's request for doing this in the authorization code block is not an appropriate use of functionality. That's not the purpose of the authorization block, and attempting to do business logic there, in the manner that the client describes, will almost certainly compromise security and result in unmaintainable code.
The authorization block is not the right place for it, but you may be able to do something fairly generic with action filters.
Load the saved data in OnActionExecuting. I'm not sure if you'll be able to get it passed to the action method as a parameter, but at the least you should be able to add it into ViewData so it can be used as a starting point for generating the model for the form page.
Not sure if the model will be available for saving before ActionExecuting, but if not the model as it exists after the action method runs should be an appropriate alternative.
I have an ASP.NET MVC web app and have a function that I need to call when the page loads only once. Right now I have the function being called from a base controller OnActionExecuted.
The issue I'm having is that this function is called multiple times - once for the page, and then multiple times for different ajax calls that I have on the page. I've tried putting the function on Controller Initialize but this is also called for each ajax call to a controller action. So is there some function in the ASP.NET MVC page cycle that only gets called when a page is requested (GET) and not for all of the ajax calls (POST)? The only thing I can think of is putting all of the ajax calls in separate controllers that don't inherit from the base controller but there must be a nicer solution.
Depending on how you're doing your AjaxRequests you could use something like the following:
public ActionResult Index()
{
if (!Request.IsAjaxRequest())
{
LoadConfiguration();
}
// Rest of Action method (snip)...
}
This works if, for instance, you're performing Ajax with the jQuery JavaScript library which sends a special token to the server to indicate that it's an Ajax call (I'm sure other libraries do this). This might be better just in case someone changes the JavaScript code to perform GET Ajax requests.
One thing you mentioned does intrigue me though "The issue I'm having is that this function is called multiple times" - actually 'technically' this is not true. It IS only getting called once....per request, all those Ajax calls are treated as separate requests by the server, as if the user is requesting a page via the web browser. It sounds like what you really need is a way to differentiate between web page HTTP Requests, and Ajax HTTP Requests, hence the above solution.
It came to me right after posting the question...
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
if (Request.HttpMethod == "GET")
{
LoadConfiguration();
}
}
You can either do this work in the application scope (on start, in global.asax or other similar approaches), or store a configuration object in the session (or a flag to identify it has already been done, if there is nothing to store).