I'm working on an ASP.NET MVC application where I need to keep updating a file in a time-interval. I will eventually be hosting this website on Windows Azure.
I was just wondering if the approach mentioned in Phil Haack's post
The Dangers of Implementing Recurring Background Tasks In ASP.NET is still the best approach or if I should look into creating a console app or so and use Azure Web Jobs to run it?
Any thoughts appreciated.
Thanks,
Daniel
You can do something like this:
private void AddHourlyTask(string task)
{
DateTime expiration = DateTime.Now.AddHours(1);
expiration = new DateTime(expiration.Year, expiration.Month, expiration.Day, expiration.Hour, expiration.Minute, expiration.Second, expiration.Kind);
OnCacheRemove = new CacheItemRemovedCallback(CacheItemRemoved);
HttpRuntime.Cache.Insert(
task,
task,
null,
expiration,
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
OnCacheRemove);
}
And then in a separate function:
public void CacheItemRemoved(string k, object v, CacheItemRemovedReason r)
{
if (k == "HelloWorld")
{
Console.Write("Hello, World!");
AddHourlyTask(k);
}
This would go into your Application_Start() function as:
AddHourlyTask("HelloWorld");
In order for this to work, you also need to add this somewhere in your class:
private static CacheItemRemovedCallback OnCacheRemove = null;
The functions would all sit in your Global.asax.cs file
You might look at scheduling a simple console app, batch file, perl script, etc. with the windows task scheduler. Depending on what it needs to do, it could be as simple as invoking a web method in your ASP.Net MVC web app.
One option is looking into Quartz.
Quartz.NET is a full-featured, open source job scheduling system that can be used from smallest apps to large scale enterprise systems.
I asked a similar question on Programmers: How do I make my ASP.NET application take an action based on time?
Accepted Answer:
A scheduled task triggered by either the Task Scheduler or Sql Server is the way to go here. But if you really want to manage it within your webapp, you might want to look at something like Quartz.NET. It will let you do scheduled tasks from the CLR. Then your challenge is "how do I make sure the AppDomain stays up to run the tasks."
Another way to do it as a scheduled task yet keep most of the "smarts" on the server is to make it a task that can be called over HTTP with some sort of authorization key. This lets you write a relatively simple program to call it -- if not a simple shell script -- and to keep most of the complexity in the web app which is likely already capable of running the task.
Either way rolling your own task manager is really a path fraught with peril.
Scheduling tasks in an ASP.NET MVC project is possible using the Revalee open source project.
Revalee is a service that allows you to schedule web callbacks to your web application). In your case, you would schedule a callback that would perform your desired action (i.e., update a file). Revalee works very well with tasks that are discrete transactional actions, like updating a database value or sending an automated email message (read: not long running). The code to perform your action would all reside within your MVC app. When your application launches for the very first time, then you would schedule the first web callback. When your application is called back to generate the report, then you would schedule the next callback.
To use Revalee, you would:
Install the Revalee Service, a Windows Service, on your server. The Windows Service is available in the source code (which you would compile yourself) or in a precompiled version available at the Revalee website.
Use the MVC-specific Revalee client library in your Visual Studio project. (There is a non-MVC version too.) The client library is available in the source code (which, again, you would compile yourself) or in a precompiled version available via NuGet.
You would register a future callback when your application launches for the very first time via the ScheduleHourlyCallback() method (this example is assuming that you need your action to run once per hour).
private void ScheduleHourlyCallback()
{
// Schedule your callback for an hour from now
var callbackTime = DateTimeOffset.Now.AddHours(1.0);
// Your web app's Uri, including any query string parameters your app might need
Uri callbackUrl = new Uri("http://yourwebapp.com/Callback/UpdateFile");
// Register the callback request with the Revalee service
RevaleeRegistrar.ScheduleCallback(callbackTime, callbackUrl);
}
When Revalee calls your application back, your app would perform whatever action you have coded it to do and your app schedules the next callback too (by calling the ScheduleHourlyCallback() method from within your controller's action).
I hope this helps.
Note: The code example above uses a synchronous version of ScheduleCallback(), the Revalee client library also supports asynchronous calls à la:
RevaleeRegistrar.ScheduleCallbackAsync(callbackTime, callbackUrl);
Disclaimer: I was one of the developers involved with the Revalee project. To be clear, however, Revalee is free, open source software. The source code is available on GitHub.
Related
I'm building as part of a larger process that analyzes the results of the build once it has completed. I used to work with XAML builds via C# code, and I had the following code:
QueuedBuild.Connect();
QueuedBuild.PollingCompleted += PrivateServerBuildRequest.BuildCompleted;
(QueuedBuild was IQueuedBuild type),
With new WebApi builds, do I have an event that let me know that the build has completed?
I found BuildCompletedEvent in Microsoft.TeamFoundation.Build.WebApi.Events but I didn't manage to find the way to use it.
Is there any equivalent to PollingCompleted event in WebApi builds? Something that'll fire once all build results are available?
One of the possible alternatives with the new REST API is to use Service Hooks. In particular, you might want to pay attention to the generic Web Hook, whcih can basically instruct VSTS to POST some JSON payload (once a certain event occurs) to some endpoint.
The endpoint can be anything: your custom home-made service hosted on-prem, OR an Azure Function, for instance. This article can give you an idea how to trigger an Azure Function in response to a VSTS event.
The 'Build Completed' event is in the list of available events.
So, to sum it all up, I would try the following in your case:
Create an Azure Function to accept the build info payload and process it accordingly
Subscribe to Build Completed event with the Web Hook and make sure the Azure Function URL is used as the endpoint
I'm working on a project with Asp.net MVC 5 and web api and SQL Server. I should implement a functionality that required scheduling.
I have some users in this system that every user can register an order and I save order information in the database.
Problem: for each order that has been registered in system after 2 minutes, I should send a message to his owner and notify him about his order status.
How should I check each order status 2 minutes after it has been registered?
Should I schedule a task per order that has been registered? (it could be more than 500 order per sec so I don't thinks it is a good solution)
I want a solution to handle this with a good performance.
Your best solution here is Hangfire
Hangfire is built for these kind of challenges. It really doesn't matter how many jobs you have. After you have configured Hangfire you can simply pass methods to the queue. You can also delay the execution with a TimeSpan
BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromMinutes(2));
You can even chain jobs, very handy if you have multiple steps in your order process:
BackgroundJob.Schedule(() => {
SomeProcessToComplete();
},TimeSpan.FromMinutes(2));
static void SomeProcessToComplete(){
//after code runs add another job to the queue
BackgroundJob.Schedule(
() => Console.WriteLine("Delayed!"),
TimeSpan.FromMinutes(2));
}
another solution could be ,each time order is saved write it to a text file on server,and at the same time add watch to notify you the text file is changed ,which will trigger your service to send message
I need to launch a monthly process to create a preformatted Text file. The data sources are in Database tables (sql server 2008 R2).
It's necessary to perform some logic (ETL) on the data and then write the txt file.
I found 2 solutions.
First is: use a scheduled SSIS where i Extract, Trasform data with logic inside a Script Task (c#) and dump the results in the txt file
Second solution is:
I could use SSIS with a script task only to call an action MVC (c# code and use WebClient.UploadData).
Data Extraction, transformation and results dumping in a txt file is totally performed in the MVC action.
I think that it should be better move the logic in a MVC action for the following pros:
Test Driven Development
Logging (with log4net)
Entity Framework
Reuse code logic of the WebApp
The future sql server migration could be simpler (dtsx migration) since the logic is outside the SSIS
I am more confident if all the application logic is in the WebApp: i prefer using SSIS only for huge ETL.
The Cons of the second solution is that it is necessary perform a security management of the MVC action call to avoid malicious calls.
QUESTION
What do you think is the best solution?
Logic inside or outside (WebApp MVC) SSIS?
I am confronted with the same. I choose to put the logic in an ASP.NET web app for the same reasons you listed.
My recommendation is to look into using hangfire.io. I think it is designed for such scenarios.
That being said you can always use the SSIS to call the MVC action. For security reason I would suggest making it a post request over https and provide some type of authentication. If it is a long running process you might have to extend the webClient to extend time. Credit for following code goes to how-to-change-the-timeout-on-a-net-webclient-object .
public class LocalWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri uri)
{
WebRequest w = base.GetWebRequest(uri);
w.Timeout = 10 * 60 * 1000;//10 minutes
return w;
}
}
usage:
using (var client = new LocalWebClient())
{}
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.
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.