Introduction

So you're almost finished with the user registration portion of your MVC project, but you need to be able to send an email to each user 3 days before their 30-day trial plans are about to expire. Now what?

Well, you could create an out-of-band process that batches up expiring user accounts nightly and sends them an email message at 4:00 in the morning. But that has hassle written all over it. First, you have to create a job outside of your web application. Next, you have to schedule that job. Then, you have to handle any errors the overnight job generates and, of course, monitor the thing yourself especially when Ted (you know the guy who always reboots the server just as he's about to walk out the door) is in charge of that other server.

There is another way.

Use your web application. That's right: it already has access to the database, it can render & send outbound email messages (you already sent out a welcome message, right?), and it's part of your testing rig. Now before you freak out about having to keep your web application loaded for days straight or some other cockamamie scheme (aka. hack), I'll say it again: there is another way.

Background

The development group I work with has run into this issue so many times that we decided to write our own solution to this problem and make it an open source project. It's called Revalee—as in reveille: a signal to arise—and it is a Windows Service that freezes your web requests in carbonite (well, not really) until it's time to thaw them out. More technically, Revalee makes note of your original web requests and calls back your web application at a preset, future date and time.

Disclaimer: In case you missed it two sentences earlier, Revalee is a free, open source project written by the development team that I am a part of. It is available on GitHub and is covered by the MIT License. If you are interested, download it and check it out.

If you take a look at the GitHub project, you will see that project has three parts: Revalee.Service, Revalee.SampleSite, and Revalee.Client. The service is the Windows Service itself and it receives your original web requests and handles scheduling your future callback. The sample site is a mini-MVC project that illustrates how someone might use Revalee within their own MVC web application. (This article will attempt to cover this same topic.) Finally, the client part of the project is a client library that helps automate sending requests to the service.

First things first. Let's install the service.

Installing Revalee

Whether you download the GitHub project and compile the C# solution yourself or you simply download the precompiled files from the project website, installing Revalee is a simple affair. First, copy the following compiled files into your desired folder (say: C:\Program Files\Revalee\):

That's it. You're done installing. One more thing: let's assume for the sake of this article that your server's IP Address is 172.31.46.200. (We'll need this later.)

As an aside, the Revalee service listens on port 46200 for scheduling requests. This is the service's default port number, but it can be changed to whatever you need it to be. However, keep port 46200 (or whatever port you've configured) in mind, especially if you have internal firewalls separating the server where Revalee is installed from your web server(s).

Using Revalee

Getting back to the problem at hand, first copy the Revalee.Client.dll file into your MVC project's bin folder. Next, add a reference to the Revalee.Client assembly to your MVC project.

There's also a Revalee.ClientNuGet package to simplify this if you prefer that route.

As before, let's assume for the sake of this article that the controller, which will be handling your callback actions, is named ScheduledCallback. At the top of that controller in your MVC application, don't forget to add the namespace reference:

using Revalee.Client;

In your ScheduledCallback controller, create a new action called, say, SendExpirationMessage that had a single, integer parameter:

public ActionResult SendExpirationMessage(int userId)

This controller action will be what your future callback will request. Using the incoming userId, you will be able to lookup any information about the specified user from the database before you have to format your outbound expiration email message à la:

Finally, create a helper method that you'll call when you need to schedule the future callback (27 days from now, in this case). This is the method that you'll call when the user registers on your site:

privatevoid ScheduleExpirationMessage(int userId)
{
// The server address where the Revalee service is installedstring revaleeServiceHost = "172.31.46.200";
// The callback will occur 27 days from now
DateTimeOffset callbackTime = DateTimeOffset.Now.AddDays(27.0);
// The url that will be called back, including userId
Uri callbackUrl = new Uri(
string.Format("http://mywebapp.com/ScheduledCallback/SendExpirationMessage/{0}", userId));
// Register the callback request with the Revalee service
RevaleeRegistrar.ScheduleCallback(revaleeServiceHost, callbackTime, callbackUrl);
}

In case you need to track it for your web application's own internal purposes, ScheduleCallback() returns a Guid. That way, you can reference the future callback using that unique identifier or simply cancel the callback (see below) if you need to.

Naturally, there's a lot more code than this, but rendered down to its essence, you can either schedule a callback or cancel one (using the Guid you received when originally scheduling the callback). All of the URLs generated are used to call a REST web service. That's it. Easy, right?

Well...

Revalee.Service

The Revalee.Service project does the real heavy-lifting. Or to keep the Star Wars references going, this is where the Ugnaughts work (oh, and don't pretend you don't know who they are). More specifically, the Supervisor class manages it all, like Lobot (zing!):

As you can see, the Supervisor class instantiates numerous sub-manager classes each in charge of handling its own domain. They are:

Class

Responsibility

ConfigurationManager

Loads configuration settings from the Revalee.Service.exe.config file

TelemetryManager

Tracks activity via the Windows Performance Monitor

StateManager

Stores the callback requests

TimeManager

Wakes the service to process a callback

RequestManager

Handles incoming requests for callbacks

WorkManager

Performs the callback to your web application

And, yes, you saw good 'ole _SyncRoot to handle locking for the multi-threaded bits.

Extensible Storage Engine (aka. JET Blue)

A word or two about persistence. As a Windows Service, Revalee needs to be able manage data persistence, since a server can be rebooted at anytime (thanks, Ted!).

For this reason, Revalee uses the tried-and-true Extensible Storage Engine (ESE), also known as JET Blue. Per Wikipedia, "the ESE Runtime (ESENT.DLL) has shipped in every Windows release since Windows 2000". That's good, because it means Revalee can rely on having access to a database without having to install anything new. Fortunately, accessing ESE from .NET was made easy due to the existence of the ESENT Managed Interface open source project.

Data persistence in Revalee is handled in the EseTaskPersistenceProvider class. Highlighted below are the AddTask() and ListTasksDueBetween() methods that are used to interact with ESE via the Esent.Interop. Naturally, this class is rather long, but it's been edited down here for the sake of brevity:

Using ESE for persistence worked out great for Revalee, since it's compact and fast.

Conclusion

As reviewed above, using Revalee with your MVC application to schedule future tasks is easy. It makes operations that are typically outside the scope of your web application instead a core part of your web application. The web callbacks received from Revalee promotes those requests to first-class status on par with everything else your MVC application does. As first-class processes, you never have to worry about your web application unloading without handling the requested actions. And that makes things like handling future expiration email messages a breeze.

Comments and Discussions

Revalee is a awesome development in scheduling category.
I have seek your suggestion for some issues which After trying for couple of days I couldn't resolve it.

I have a actionresult which needed to be hit every 15 minutes for sending e-mails.I have followed the blog and implemented it properly its working fine in Visual studio running environment.BUT when I host it IIS its not working. I have changed web config, revalee config and restarted the service.Still not able to figure out.If you can share any suggestion it will help me a lot.