Last week I had the opportunity to attend a workshop on the DNN scheduled task. I was there because I was giving a couple of presentations for my good friends at the SDN (http://www.sdn.nl). The workshop was given by Leigh Pointer and he showed us how to make a scheduled task as well as some ins and outs on how the scheduler is administered in DNN. The former I was familiar with, the latter was more at the edge of my radar. I have made minimal use of scheduled tasks until now, mostly because they run ‘out-of-process’. To understand the ramifications of this, you need to understand a little more about threading and the way IIS runs your application. Without going into too much detail here, suffice to say that your regular application runs in a thread that was triggered when a client browser hit your site. This triggers a plethora of components to do some work. Notably authentication, setting environment variables, handling cookies, url rewriting, etc. All this runs without you (if you’re a module developer like me) being very much aware of this.
The scheduled task does not, however, run in the same way. Most importantly it does not have an HttpContext available. Huh? And? Well, the httpcontext is used to construct urls for instance. The following bit of code is pretty typical:
DNNTree.ImageList.Add(ResolveUrl("~/images/file.gif"))
In the ResolveUrl method the ‘~/’ will be replaced by the path to your application. In most cases this will be ‘/’ so the browser will look for www.mydomain.com/images/file.gif. But it gets more tricky with child portals. Anyhow, the point is that this cannot be done without the processor being aware of what url it is on right now. You cannot use this like this in your scheduled task.
OK, you say, but I don’t need this. Sure. But now let’s take a ‘real world’ example from the Document Exchange lab. Recently I have revised the way email notifications are done and have rewritten this part of the application to ‘harvest’ notifications and send them out in a single email. This greatly reduces the amount of emails users will receive. This was requested by customers using DMX in very active settings (lots of changes and ‘subscriptions’ to content). A scheduled task runs every so often and looks for what it needs to send and compiles emails to send. Now, this is where the pain starts. Because deep down in the DNN library many methods rely on httpcontext being there. The two most important culprits in my solution were Localization and TokenReplace. Both make extensive reference to the ‘Current Portal Settings’. This is a seven headed monster of settings that is stored in http context in the url rewrite module (line 463 in DNN v 04.08.04):
' load the PortalSettings into current context
Dim _portalSettings As PortalSettings = New PortalSettings(TabId, objPortalAliasInfo)
app.Context.Items.Add("PortalSettings", _portalSettings)
It relies on knowing the currently requested TabId and the portal alias being used. This is not available outside http context. So it is pointless to rely on, or even to try and reconstruct portal settings in the scheduled task. But I’ll list just a couple of methods that rely on this in the aforementioned components:
Private Shared Function GetResource(ByVal ResourceFileRoot As String, ByVal objPortalSettings As PortalSettings, ByVal strLanguage As String) As Hashtable
Public Shared Function GetString(ByVal name As String, ByVal ResourceFileRoot As String, ByVal objPortalSettings As PortalSettings, ByVal strLanguage As String, ByVal disableShowMissingKeys As Boolean) As String
TokenReplace: Public Sub New(ByVal AccessLevel As Scope, ByVal Language As String, ByVal PortalSettings As PortalSettings, ByVal User As UserInfo, ByVal ModuleID As Integer)
It is clear you cannot use these classes without some serious hacking. I ended up copying whole sections of the core into DMX to eliminate the need to portal settings. I even ended up replacing PortalInfo as it does not support IPropertyAccess. Instead, PortalSettings (sigh) is used to replace tokens.
In conclusion PortalSettings is used nearly everywhere in the framework. In its current form it is unavailable outside http context making most of the framework unusable in a scheduled task. In my opinion the PortalSettings object and its use needs to be made more robust to protect it from blowing up outside http context. Secondly it needs to be stored in a place where you can get at it in or outside context.