昨天看到erictang2003给我的一个留言,其中提到了一个乍一看很古怪的问题:
以下代码中用自定义class填充ArrayList:
为何当数据源被Cache缓存后,<%# ((MyInfo)Container.DataItem).Item %>就不成功,抛出"System.InvalidCastException: 指定的转换无效" ,但是如果不用Cache缓存,就可以成功?
(代码略,请见原始内容)
我试验了一下,还真遇到了这个问题。不过简单分析了一下原因,发现实际上与Cache无关(存在Session里面也一样),而且仅当把自定义类写在.aspx页面中的情况下才发生(在code-behind中就不会出问题)。这让我想到了ASP.NET Runtime在动态编译时的一个行为,即每次.aspx页面文件变化时都将自动重新编译出新的assembly(可以观察%SYSTEMROOT%\Microsoft.NET \Framework\v1.x.xxxx \Temporarily ASP.NET Files \virtualroot下面的dll文件)。为了确认这就是问题的原因,我清空了所有已经编译好的页面assembly,然后访问你写的这个页面(第一次访问将写入Cache),然后刷新页面(这次应该就是从Cache中取得),没有任何问题!然后将页面文件略作修改再次保存,访问(此时ASP.NET将重新编译出一个新的assembly),类型转换出错了!
实际上,如果你在页面上显示一下Container.DataItem.GetType().AssemblyQualifiedName和typeof(MyInfo).AssemblyQualifiedName就发现问题了。原来存在Cache中的对象还是原来的那个assembly中的类型,而再次数据绑定时你将它转换为的MyInfo是在新的assembly中的类型!虽然类型的全称是相同的,但是由于它来自于两个不同identity的assembly,因此CLR并不认为他们是同样的类型。而当使用code-behind的时候,你的自定义类型所在的assembly并不会随着.aspx文件的修改而变化,因此Cache中存的对象和你将要转换的对象也是一致的。这就是所谓的强类型系统喽(相对而言的有些弱类型系统可能只是通过比对类型的文本相等就认定类型相等)。
总之,又是一个值得注意的问题。:)