提醒:本页面将不再更新、维护或者支持,文章、评论所叙述内容存在时效性,涉及技术细节或者软件使用方面不保证能够完全有效可操作,请谨慎参考!

最近要改进一个旧项目,项目采用ASP(JScript)编写,由于时间问题一直没有重构,所以还是ASP将就着,考虑到效率问题,准备把部分内容缓存起来,当然直接读写Application缓存不是很妥当的,遂采取缓存字典对象( Scripting.Dictionary )来实现。好了,一般会认为代码如下所示:

var key = "dict_cache"
var obj = Server.CreateObject("Scripting.Dictionary");

Application.Lock();
Application.Contents(key) = obj;
Application.Unlock();

偏偏事与愿违,如果你这样做的话,IIS会报“ 不允许的对象使用方式 ”错误,具体内容如下:

Application 对象 错误 'ASP 0197 : 80004005' 

不允许的对象使用方式 

/index.asp,行 12 

不能将具有单元模型行为的对象添加到应用程序的内部对象中。

同样的让我们回到VBScript,编写ASP/VBScript脚本代码如下:

Dim key
key = "dict_cache"
Application.Lock
Set Application(key) = _
	Server.CreateObject("Scripting.Dictionary")
Application.Unlock

错误依旧,也就是说我们不能将形如Scripting.Dictionary的对象存储到Application中,这是多么沮丧啊,真的没有办法吗?

下面我给出两个解决办法供大家参考。

1. 使用Application.StaticObjects

其实我们可以变通一下,我们可以将字典对象存储到Application.StaticObjects中,当然这个有点小麻烦,我们需要创建Global.asa文件,内容如下:

<object runat="server" scope="application" id="dict_cache"
progid="Scripting.Dictionary"></object>

注意这里的id所指示的值 dict_cache ,然后我们就可以这样操作了:

Dim key
key = "dict_cache"
Application.Lock
Set Application.StaticObjects(key) = _
	Server.CreateObject("Scripting.Dictionary")
Application.Unlock

我在 《ASP/WScript/VBScript实现ActiveX COM对象管理器(2)》 的实现中也简单介绍过这个办法。

2. 使用VBScript数组

肯定有同学不希望使用 Global.asa 文件,那这里还有个办法可以变通一下,那就是VBScript数组,等等,我为什么强调VBScript数组,难道JScript数组不可以吗?别急,答案即将揭晓。

在ASP/VBScript使用数组解决问题的实现代码如下,简单的说就是将对象用Array包装一下:

Dim key
key = "dict_cache"
Application.Lock
Application(key) = _
	Array(Server.CreateObject("Scripting.Dictionary"))
Application.Unlock

看样子我们已经成功将字典对象存入Application缓存中了,不过大家在读取时要注意一点,那就是读取要取数组内容,加个下标0,VBScript代码如下:

Application("dict_cache")(0).Add "test", "Hello World!"
Response.Write Application("dict_cache")(0).Item("test")

接下来我来说明为什么JScript数组不可以,因为Application不可以存储对象,而JScript与VBScript不同的是:JScript中一切皆为对象,包括其数组Array,VBScript的数组不是对象(至少没有对象应该有的方法)。所以对于JScript我们不能采用这个技巧。

深入了解过JScript的同学一定知道有个VBArray(),呵呵,这个只是微软为了兼容VBScript而提供的一个访问VB数组的方法,说白了这玩意儿只能读取创建好的VB数组,而不能自己创建一个VB数组。

解决的办法到这儿变得清晰起来,也就是说我们要想方设法的建立一个VB数组,当然这事儿还得VB/VBScript来做,我们可以使用 MSScriptControl.ScriptControl 组件在JScript调用VBScript创建这个特殊的数组:

var dict = Server.CreateObject("Scripting.Dictionary");
var msscript = Server.CreateObject("MSScriptControl.ScriptControl");
msscript.Language = "VBScript";
msscript.AllowUI = false;
msscript.AddObject("SafeVBArrayValue", dict);

var key = "dict_cache"
var obj = Server.CreateObject("Scripting.Dictionary");

Application.Lock();
Application.Contents(key) = msscript.Eval("Array(SafeVBArrayValue)");
Application.Unlock();

测试无误,说明这个方法是可行的,typeof这个VB数组,显示类型为 Unknown ,看来JScript也不知道这个是什么类型。

最后我放出在这次项目中用到的部分封装代码供大家参考:

var RawCache = function() {
    this.setItem = function(key, value) {
        Application.Lock();
        Application.Contents(key) = value;
        Application.Unlock();
    }

    this.getItem = function(key, defvalue) {
        value = Application.Contents(key);
        return typeof(value) != 'undefined' ? value : defvalue;
    }
    
    this.remove = function(key) {
        Application.Lock();
        Application.Contents.Remove(key);
        Application.Unlock();
    }
    
    this.isAvailable = function() {
        return true;
    }
}

var TinyCache = function(name, useStaticObjects) {
    if (useStaticObjects) {
        this.cache = Application.StaticObjects(name);
    }
    
    if (!this.cache) {
        this.raw_cache = new RawCache();
        this.cache = this.raw_cache.getItem(name, null);
        
        if (this.cache != null) {
            this.cache = this.cache.getItem(0);
        } 
        
        if (!this.cache || typeof this.cache != "object") {
            this.mem = Server.CreateObject("Scripting.Dictionary");
            var msscript = Server.CreateObject("MSScriptControl.ScriptControl");
            msscript.Language = "VBScript";
            msscript.AllowUI = false;
            msscript.AddObject("SafeVBArrayValue", this.mem);
            this.raw_cache.setItem(name, msscript.Eval("Array(SafeVBArrayValue)"));
            this.cache = this.raw_cache.getItem(name, null).getItem(0);
        }
    }
    
    this.setItem = function(key, value) {
        if (this.cache.Exists(key)) {
            var prevalue = this.cache.Item(key);
            this.cache.Item(key) = value;
            return prevalue;
        } else {
            this.cache.Add(key, value);
            return null;
        }
    }

    this.getItem = function(key, defvalue) {
        if (this.cache.Exists(key)) {
            return this.cache.Item(key);
        } else {
            return defvalue;
        }
    }
    
    this.remove = function(key) {
        if (this.cache.Exists(key)) {
            this.cache.Remove(key);
        }
    }
    
    this.contains = function(key) {
        return this.cache.Exists(key);
    }
    
    this.isAvailable = function() {
        return true;
    }
}

使用的方法很简单,比如这样:

var cache = new TinyCache('dict_cache');

Response.Write(cache.setItem("Key1", "Value1"));
Response.Write(cache.getItem("Key1", "N/A"));
cache.remove("Key1");
Response.Write(cache.getItem("Key1", "N/A"));

好了,到这里就介绍结束了,当然ASP确实有些老旧了,如果不是这次项目修改,我也很长时间没有用ASP做项目了,哎,廉颇老矣尚能饭否啊。

参考文档