Scheduled Tasks in MonoRail: The Quick & Dirty solution

time to read 3 min | 479 words

I need to develop a set of tasks that would run in internals. Now, we need to do several dozens of those, and at that point, it is not important how we actually schedule them, we can defer that decision. But we really need to be able to start develop them soon.

So, I came up with this idea. The basic structure is this:

[OccuresEvery(Occurances.Day)]
public class SendBirthdayEmails : ScheduledTask
{
	public override void Execute()
	{
		foreach(Employee emp in Repository<Employee>.FindAll(Where.Employee.Birthday == DateTime.Today)
		{
			Email
				.From(Settings.Default.HumanResourcesEmail)
				.To(emp.Email)
				.Template(Templates.Email)
				.Parameter("employee", emp)
			.Send();
		}
	}
}

This is not really interesting, but the rest is. Remember that I don't want to deal with deciding how to actually schedule them, but we need to be able to run them right now for test / debug / demo purposes.

In my windsor.boo:

//Controllers
controllersAssembly = Assembly.Load("MyApp.Web")
for type in controllersAssembly.GetTypes():
	continue if type.Name == "ScheduledTasksController"
	continue if not typeof(Controller).IsAssignableFrom(type)
	IoC.Container.AddComponent(type.FullName, type)

//register scheduled tasks
scheduledTasksAssembly = Assembly.Load("MyApp.ScheduledTasks")
scheduledTasks = []
for type in scheduledTasksAssembly.GetTypes():
	continue if not typeof(ScheduledTask).IsAssignableFrom(type)
	IoC.Container.AddComponent(type.FullName, type)
	scheduledTasks.Add(type)
//register scheduled tasks controller independently, since it requires special configuration 
Component("ScheduledTasksController", ScheduledTasksController,
	scheduledTasks: scheduledTasks.ToArray(Type) )

So it will automatically scan the scheduled tasks assembly, and the only thing that I have left is to write the ScheduledTasksController. This is a very simple one:

image

It has just two methods, one to list all the tasks, and the second to execute them. This is strictly a dev only part of the application, so I took most of the available shortcuts that I could. So the UI looks like this:

image

And the view code is:

<% for task in tasks: %>
<tr>
	<td>
	${Text.PascalCaseToWord(task.Name)}
	</td>
	<td>
	Every ${task.OccuranceEvery}
	</td>
	<td>
	${Html.LinkTo("Execute", "ScheduledTasks", "Execute", task.FullName)}
	</td>
</tr>
<% end %>

I really like the PascalCaseToWord helper, really nice.

On the controller's side of things, I have this:

public ScheduledTasksController(Type[] scheduledTasks)
{
	scheduledTaskDescriptions = new ScheduledTaskDescription[scheduledTasks.Length];
	for (int i = 0; i < scheduledTasks.Length; i++)
	{
		scheduledTaskDescriptions[i] = new ScheduledTaskDescription(scheduledTasks[i]);
	}
}

public void Index()
{
	PropertyBag["tasks"] = scheduledTaskDescriptions;
}

Not a best practice code, but I did knock the whole thing very quickly. ScheduledTaskDescription just takes a type and unpack it in terms of attributes, name, etc.

The end result is that the other developers on my team can add a new scheduled task by simply adding a class that inherits from ScheduledTask, go to the browser, hit F5 and start executing it.

Now that is RAD.