[转载]重温delphi之:如何快速开发原生ActiveX控件

mikel阅读(970)

[转载]重温delphi之:如何快速开发原生ActiveX控件 – 菩提树下的杨过.Net – 博客园.

ActiveX技术虽然是一项古老的技术,但是却有着广泛的应用,支付宝的密码输入控件,各大银行的密码输入控件,网页聊天室中的截屏功能,网页播 放器中的p2p播放…甚至Flash,Silverlight等等,在IE中都表现为ActiveX。虽然C#也能开发”用于网页的com应用”,能 达到类似ActiveX的效果(可参见C#中开发ActiveX的学习笔记),但是有一 个要命的问题是必须得安装几百M的.net framework框架,如果仅仅为了安全的输入一个密码,而要用户下载几百M的安装程序,这是很多人不能接受的,delphi做为win32下的原生开 发工具,能很好的支持微软各种”古老”的经典技术。(再做点小广告:delphi的kyrix版本还能编译跨平台的应用哦!)

ok,开工吧:

开发工具:推荐用delphi 2010(d7也可以,不过添加属性,方法等过程要手动,稍微麻烦点)

1.启用 delphi2010–>File->New->Other–>Active Library

2.项目命名为MyActiveX

3.File–>Save All 全部保存

实际上这样就能编译了,不过只是空的dll

4.File–>New–>Other–>Active Form

改名为MyForm

将对应的单元文件,保存为UMyForm.pas

5.打开MyAcitveX.ridl文件,切换到design视图,选中IMyForm接口,右击New–>Property 添加一个属性Msg

将Msg属性的Type改为BSTR 即WideString类型

完了之后,点击工具栏中的Refresh Implementation(即上图中工具栏中圈起来的部分)–这一步很重要,点击之后,它将自动生成属性Msg对应的声明和实现代码模板

6.打开UMyForm.pas–即ActiveForm对应的单元,找到Set_Msg 以及Get_Msg的实现部分,补充代码如下:

function TMyForm.Get_Msg: WideString;
begin
result:
=_msg;
end;

procedure TMyForm.Set_Msg(const Value: WideString);
begin
_msg :
= value;
end;

当然TMyForm的private部分,得先加一个私有成员

type
TMyForm
= class(TActiveForm, IMyForm)
private
{ Private declarations }
_msg:WideString;

这样我们就为即将生成的ActiveX控件,添加了一个字符串类型的属性Msg,下面来测试一下:

7.编译项目,会生成一个MyActiveX.ocx,在运行栏里输入

regsvr32 C:\Users\jimmy.yang\Desktop\delphi_activex\MyActiveX\MyActiveX.ocx

注:这里ocx的路径,请各位根据自己的实际路径修改

这样就完成了ocx的注册。

8.放到html里测试一下:

代码

<OBJECT ID=’x’ name=’x’ CLASSID=’CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659′ align=center hspace=0 vspace=0 ></OBJECT>
<script type=’text/JavaScript>
var x = document.getElementById(x);
alert(x.Msg);
</script>

关于CLSID在哪里查看,打开:MyActiveX_TLB.pas文件,定位到下面这里:

代码

const
// TypeLibrary Major and minor versions
MyActiveXMajorVersion
= 1;
MyActiveXMinorVersion
= 0;

LIBID_MyActiveX: TGUID = {49138437-8265-4B1A-9EAE-D0F615D68464};

IID_IMyForm: TGUID = {54A20855-29A3-4C92-85DE-A419DA457C7A};
DIID_IMyFormEvents: TGUID
= {60BBC967-E1E6-4E98-BAE5-776BFD06E9CC};
CLASS_MyForm: TGUID
= {52D17094-0687-4A2F-B2DB-30F3189AC659};

其中 CLASS_MyForm: TGUID对应的就是ClassID

运行后,除了弹出一个空白的警告框,暂时看不到其它:)(可不就是这样么?Msg属性没给任何初始值,当然是空字符串,所以弹出一个空的警告框是正 常的)

9.我们再来添加一些控件和方法,以验证刚才设置的属性确实有效

在MyForm上添加一个文件框,一个按钮


按钮的事件如下:

procedure TMyForm.Button1Click(Sender: TObject);
begin
_msg:
= self.Edit1.Text;
end;

即把文本框的值赋给属性Msg

再继续定位到Set_Msg,略做修改

procedure TMyForm.Set_Msg(const Value: WideString);
begin
_msg :
= value;
self.Edit1.Text :
= _msg;
end;

即设置Msg属性时,同时也把值显示在文本框里,以便等会儿我们好测试在js中给activeX属性赋值的效果

ok了,再来测试一下,编译一下,如果通不过,请先运行

regsvr32 C:\Users\jimmy.yang\Desktop\delphi_activex\MyActiveX\MyActiveX.ocx /u

将刚才注册的ocx反注册,同时关掉浏览器,不然该ocx文件一直被占用,无法更新.

修改一下html的代码:

代码

<OBJECT ID=’x’ name=’x’ CLASSID=’CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659′ align=center hspace=0 vspace=0 ></OBJECT>

<hr />
<input type=’button’ value=’显示Msg属性的值’ onclick=’ShowMsg()’/>
<input type=’button’ value=’设置 Msg属性的值’ onclick=’SetMsg()’/>
<script type=’text/JavaScript>
var x = document.getElementById(x);

var ShowMsg = function(){
alert(x.Msg);
}

var SetMsg = function(){
x.Msg
= js传过 来的值;
}
</script>

运行效果:

10.添加Method
我们已经知道了如何给ActiveX添加对外 公开的属性,但是光有属性显然不够,我们再添加一个Method,参考第5步中的截图,选择new–>Method,添加

一个方法,命名为ShowMsg,Return参数项用默认值HRESULT,然后Parameters添加一个参数,如下图:

同样不要忘记了点击工具栏中的更新按钮,再打开UMyForm.pas,会发现自动添加了一个过程的定义:

procedure ShowMsg(const p: WideString); safecall;

转到它的实现部分,写几行测试代码:

procedure TMyForm.ShowMsg(const p: WideString);
begin
showmessage(
Msg属性的值为: + _msg + #13 + 传入的参数为: + p);
end;

再编译,html代码中添加一些代码:

代码

<OBJECT ID=’x’ name=’x’ CLASSID=’CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659′ align=center hspace=0 vspace=0 ></OBJECT>

<hr />
<input type=’button’ value=’显示Msg属性的值’ onclick=’ShowMsg()’/>
<input type=’button’ value=’设置 Msg属性的值’ onclick=’SetMsg()’/>
<input type=’button’ value=’调用 ShowMsg方法’ onclick=’CallShowMsg()’/>
<script type=’text/javascript’>
var x = document.getElementById(x);

var ShowMsg = function(){
alert(x.Msg);
}

var SetMsg = function(){
x.Msg
= js传过 来的值;
}

var CallShowMsg = function(){
x.ShowMsg(
这是js 传过来的参数);
}
</script>

运行看下:

类似的,我们还可以为ActiveX添加带返回值的function,而非过程procedure,但是比较郁闷的是,我试了半天,delphi中 编译正常后,但是在javascript中就是无法取得返回值,估计是delphi的变量类型与javascript的变量类型不匹配引起的,哪位 delphi高人如果知道原因,还请指点一二,在此先谢过.

11.深入看下ActiveX中到底有哪些玩意儿?

既然ActiveX能加载到网页中,肯定也是dom树的一份子了,想知道ActiveX到底提供了哪些其它属性或方法吗?以下的js代码可以测试出 来:

代码

<div id=”info”></div>

<script type=”text/javascript”>

var _info=“”;

for(var p in x){
_info
+= p + + x[p] + <br/>;
}

document.getElementById(info).innerHTML = _info;

</script>

当然如果你用IE8的js调试功能,也能看到刚才定义的那些方法和属性:

注意一下这里还有其它很多属性,比如Caption,所以你在js中用alert(x.Caption)也能弹出ActiveForm的标题,这是 我们通过IE/JS从外部来看ActiveX的,其实也能换个角度从delphi内部看下activex的结构,com技术号称就是一组通用的接口规范, 所以我们在delphi内部确实也能发现不少接口:

MyActiveX.ridl中可以看到

library MyActiveX
{

interface IMyForm;

表明IMyForm就是一个接口,再定位到MyActiveX_TLB.pas可以发现:
type


IMyForm = interface;

MyForm = IMyForm;

IMyForm = interface(IDispatch)

说明MyForm就是从IDispatch继承下来的一个接口

最后再到UMyForm.pas中可以看到
type
TMyForm = class(TActiveForm, IMyForm)
Edit1: TEdit;

说明最终的运行窗口,就是继承自TActiveForm并实现了IMyForm的一个类

12.事件支持

打开MyActiveX.ridl,查看IMyFormEvents部分,可以看到delphi生成的ActiveX控件中已经预置了很多事件

dispinterface IMyFormEvents
{
properties:
methods:
[id(0x000000C9)]
void OnActivate(void);
[id(0x000000CA)]
void OnClick(void);
[id(0x000000CB)]
void OnCreate(void);
[id(0x000000CC)]
void OnDblClick(void);
[id(0x000000CD)]
void OnDestroy(void);
[id(0x000000CE)]
void OnDeactivate(void);
[id(0x000000CF)]
void OnKeyPress([in, out] short* Key);
[id(0x000000D0)]
void OnMouseEnter(void);
[id(0x000000D1)]
void OnMouseLeave(void);
[id(0x000000D2)]
void OnPaint(void);
};

我们可以用javascript来响应这些事件,比如就拿我们最熟悉的OnClick事件,js中要这么处理:

<OBJECT ID=’x’ name=’a’ CLASSID=’CLSID:52D17094-0687-4A2F-B2DB-30F3189AC659′ align=center hspace=0 vspace=0 ></OBJECT>

<script type=”text/javascript” event=”OnClick” for=”a”>
alert(‘你 点击了ActiveX控件’);
</script>

运行后,鼠标在ActiveX的空白处点击,会弹出一个警告框:”你点击了ActiveX控件”

13.其它问题

(1)delphi2010中的function问题

前面提到了带返回值的function不好弄,其实这个不是什么大问题,完全可以迂回用procedure与属性解决

比如我们可以定义一个带参数的procedure,js调用时传入参数,然后在procedure内部,对参数进行处理后,将其赋值为 ActiveX的任何一个类型匹配的属性,比如前面提到的Caption属性,然后js获取Caption属性,相当于就是ActiveX处理后的返回值 了

(2)d7中如何添加属性和方法
d7中没有 New–>Method之类的菜单,要添加属性/方法的话,得手动半自动化输入,步骤:先选中xxximp对应的单元,利用Edit菜单下的 Add to Interface功能,参考下图:

然后在弹出对话框里,手动输入要添加的属性/方法声明,参考下图:


另外在我本机测试,发现d7中添加的function,js能正常 调用,不知道是不是我机器的个别现象

注:截图不易,打字很累,允许转载,但请注明来自菩提树下的杨过 http://www.cnblogs.com/yjmyzz/archive/2009/12/16/1625559.html

源代码下载:http://files.cnblogs.com/yjmyzz/delphi_MyActiveX.rar

作 者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]delphi开发activex控件的心得

mikel阅读(1174)

[转载]delphi开发activex控件的心得 – 601880 – 51CTO技术博客.

最近在做的一个 B/S 系统要在客户端读取 USB 锁的要求,所以就需要写一个 OCX 来验证。原来也没有做过,所以折腾了好几天,总算有点眉目了,所以把心 得写下,交流下。
首先要弄明白你要写的 OCX 是用在客户端还是用在服务器端
假如用在客户端:
1、 创建
打开 delphi 7,选择菜单“new”>“other”>“activex”>“active form”>输入项目名称,系统自动给你创建了3个文件,一个是项目文件,一个是 form 的单元文件,还有一个后缀带 TLB 的文件。这三个文件中的你只需要更改 form 的单元文件,项目文件一般不 需要写什么,TLB 文件有系统自动维护。
2、 增加接口函数
所谓接口函数就是调用 OCX 的程序可以直接调用的函数,如果你要写的函数不提供外部函数可以按常规 声明就可以。但要声明接口函数则必须按步骤声明,有两种途径,其一:选择 form 单元文件,然后点击“edit”>“add to interface”,弹出一个对话框,输入要声明的函数,如:function fun:integer;其二:选择 form 单位文件,然后点击“view”>“Type Library”,弹出一个对话框,然后点击工具栏中“add a interface”可以增加函数或过程。说明:在这里注意几个地方,第一,一定要选 form 文件才可以,否则相应的菜单 都是不可用的;第二、声明函数或过程的参数类型要注意 string>BSTR(第二种途径)或 WideString(第一种途径)。增加属性也函数类同。
3、 调试
我是用html调试的,所以可用在 delphi 种 配置run的参数 “run”>“parametes”,弹出一对话框,在“host application”中输入C:\Program Files\Internet Explorer\IEXPLORE.EXE “parametes”http://192.168.6.9/my.htm,这样就可以调试 Delphi 代码了
4、 发布
在没有 build 前,可以“project”>“web deployment”中配置(主要输入 ocx 的 地址和 web 服务器的 url),然后可以“web deploy”发布,其实就是生成了一个 htm 文件,不过该文件里包含了调用方法而已。但是 delphi 好 像至运行执行一次,发布后这两个菜单就不可用了,甚至刚开始我不知道,直接编译了,编译后该菜单也不可用了。说明:如果没有用“web deploy”发布,而想在网页中调用的,就要知道该 ocx GUID,可以在 TLB 文件中找,CLASS_XXX 后 面的就是,这一点也需要注意。
5、 数字签名
要想网页能够顺利使用 ocx,就需要数字签名,否则IE直接就给干掉了,当然IE安全级调低 除外。关于生成测试数字签名的方法,网上很多。我想要说得是,测试数字证书只能用在 win2000系统,用在 XP,特别是打了补丁的 XP,根本不行。另外也可以参考《使用 Delphi 快速开发 ActiveX 控件》这篇文章如果用在服务器端:其实应该是 COM 组件了吧,asp 的调用方式是
<%
set obj=Server.CreateObject(“DelphiCom.Test2”)
%>
a
、创建
打开 delphi 7,选择菜单“new”>“other”>“activex”>“active libray”生成一个项目,然后再
“new”
>“other”>“activex”>“automation object”,然后参照上面增加接口函 数,就可以。不过编译出来是个 dll,但是是可以注册的 dll
“automation object”
也可以换成“com object”,但两者是有区别的,具体可以网上查询。

[转载]asp.net mvc 正常支持Fckeditor编辑器

mikel阅读(930)

[转载]asp.net mvc 正常支持Fckeditor编辑器 – Mr.d’s Time – 博客园.

在接近下班的时候,快速的浏览了博客园的首页,被该贴“建 站专家:让 MVC 支持 CuteEditor Using CuteEditor under MVC”中的话题“ASP.NET mvc无法正常支持Fckeditor编辑器”所吸引,由于当前的开发环境也正式ASP.NET mvc 2.0,因此快速的做了一下“试探性的测试”,测试的结果很满意—-成功。因此,早早就回家进行详细的测试。

ASP.NET mvc为什么无法正常使用Fckeditor等控件?

在asp.net中,页面的表现形式与逻辑代码(cs)使用了(codebehind)代码分离技术,表面上他们是各不相干,实际上它们就是一根绳 子上的蚂蚱栓在一起的。因此我们在(cs)中可以直接使用控件的ID来获取或设置它相应的值。

而在asp.net mvc中,根据mvc模式,我们能非常清楚他们之间只有联系或通信,而不是一个整体,因此我们无法向asp.net那样去通过控件的ID获取或设置相应的 值。

ASP.NET MVC Post提交的应用

在asp.net中一个Post一下直接是回传给当前页面,而asp.net mvc的任何动作都是直接对应Control中的Action,因此在asp.net mvc中必须有对应的Aciton来接受请求,那表单的数据怎么传递呢?根据我平常的应用,使用这2种方法:

1、通过Action的参数直接对应着表单中的name,实例代码如:

代码

[AcceptVerbs(HttpVerbs.Post)] public ActionResult Add(string txtTitle//表单中控件的值必须相等,string txtContent) { string title = txtTitle; string content = txtContent; return View(); }

2、通过Action定义FormCollection参数接受,实例代码如:

代码

[AcceptVerbs(HttpVerbs.Post)] public ActionResult Add(FormCollection form) { string title = form["txtTitle"]; string content = form["txtContent"]; return View(); }

获取与设置Fckeditor控件的值

首先,测试项目目录结构:如图

获取Fckeditor内容:Add(View)视图:

代码

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> <%@ Register Assembly="FredCK.FCKeditorV2" Namespace="FredCK.FCKeditorV2" TagPrefix="FckV2"%> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> Add </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <%using (Html.BeginForm()) { %> <h2>添加</h2> <h4>标题</h4> <p><%=Html.TextBox("Title","defualt")%></p> <h4>内容</h4> <FckV2:FCKeditor runat="server" BasePath="/Content/" ID="content"></FckV2:FCKeditor> <input type="submit" value="添加" /> <%} %> </asp:Content>

获取Fckeditor内容:对应Add视图POST提交方式的Action:

代码

[AcceptVerbs(HttpVerbs.Post)] public ActionResult Add(FormCollection form) { string title = form["Title"]; string content = form["ctl00$MainContent$content"]; return RedirectToAction("GetArticle"); }

测试取值效果图:

设置Fckeditor内容:GetArticle(View)视图:

代码

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MvcFckTest.Entity>" %> <%@ Register Assembly="FredCK.FCKeditorV2" Namespace="FredCK.FCKeditorV2" TagPrefix="FckV2"%> <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> GetArticle </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> <h2>GetArticle</h2> <% using (Html.BeginForm()) {%> <fieldset> <legend>Fields</legend> <p> <label for="Content">Content:</label> <FckV2:FCKeditor runat="server" BasePath="/Content/" ID="content"></FckV2:FCKeditor> </p> </fieldset> <% } %> <div> </div> <script> window.onload = function() { document.getElementById("<%=content.ClientID%>").setAttribute("value", "<%=Model.Content %>"); } </script> </asp:Content>

设置Fckeditor内容:对应GetArticle视图Action:

public ActionResult GetArticle() { Entity model = new Entity() {Title="测试Fck",Content="Jeffrey.Dan,测试"}; return View(model); }

测试效果图:

总结

通过上面的测试,我们发现无需修改或扩展使用第三方插件就能使asp.net mvc正常支持Fckeditor编辑器,同时也发现,如果将Fckeditor服务器控件用已经转换过后的Html代码代替,使用起来更加方便,这个任 务就留个大家把,哈哈。

[转载]带有完美中文帮助的 Flash Builder 4 正式版下载及序列号

mikel阅读(849)

[转载]带有完美中文帮助的 Flash Builder 4 正式版下载及序列号 » Y.Boy’s Blog.

记得很早很早之前,7yue在群(76488373)里发过 Flash Builder 4 正式版的欢迎界面。那时只能观望,现在已能触摸了。Flash Builder 4 的变化让人感到莫大的惊喜和高兴。不只是那造型变得非主流了,帮助文档也是管理得井井有条。而这次,所有帮助文档都有中文版本了,可以在线查看(链接到 Adobe的网站),也可以下载到本地进行脱机查看。全部工作都由“Adobe Community Help”窗口进行统一管理。还有一个细节,在“帮助”菜单里多了一个陌生的“omniture”选项。下面送上截图。

下载地址(需要登录):http://trials.adobe.com/AdobeProducts/FLBR/4/win32/FlashBuilder_4_LS10.exe
万能序列号:1424-4008-9664-3602-3439-1711 (为什么是万能?因为 Flash Builder beta 时代也是用它的,一直到现在也能用)
很全的在线帮助文档http://help.adobe.com/zh_CN/Flex/4.0/UsingFlashBuilder/index.html(网 页的右上角有提供PDF下载的,见“查看帮助 PDF (6.8MB)”)

下载帮助文档到本地,不知道下载到哪个目录下呢?

[转载]MVP(SC),MVP(PV),PM,MVVM 和 MVC 表现模式架构对比

mikel阅读(1117)

[转载]MVP(SC),MVP(PV),PM,MVVM 和 MVC 表现模式架构对比 – Beginor – 博客园.

翻译 CodeProject 上的一篇文章,对常见的几种表现模式 (Presentation patterns) 进行了说明,并进行对比。原文地址是http://www.codeproject.com/KB/aspnet/ArchitectureComparison.aspx

表现模式 (Presentation patterns) 背景

与用户界面 (UI) 相关的最大的问题就是大量的凌乱的代码,主要是由这两个因素造成的,首先是用户界面包含负责的逻辑用于维护界面相关对象,其次也包含了应用程序状态的维 护。表现模式 (Presentation patterns) 就是围绕如何移除用户界面的复杂性,让界面更加简洁和可管理而产生的,下图就是常见表现模式的种类与分类:表现模式的种类与分类

用户界面的3大问题:状态 (State) , 逻辑 (Logic) ,同步 (Synchronization)

  • 状态 (State) : 状态是用户界面最关心的问题之一。状态是用户界面数据的当前快照,在 Web 应用中,可能是 Session 级别的一个变量,在 Windows 应用中, 则可能只是界面级别的数据。 用户界面包含的状态越多, 则用户界面越复杂。
  • 逻辑 (Logic) : 用户界面往往包含界面逻辑,例如维护文本框、组合框或者其它任何界面元素,用户界面中这种逻辑越多,则用户界面越复杂。
  • 同 步 (Synchronization) : 用户界面通常需要和业务组件协作,因此用户界面需要在界面元素与业务对象之间同步数据,如果用户界面包含的同步任务越多,则用户界面越复杂。

这三大问题与用户界面的关系如下图:

3Bigproblems

表现设计模式 (Presentation Design Pattern)

表现设计模式有助于解决上面列出的问题, 它的的基本逻辑就是创建一个额外的表现类 (Presenter) ,用来消化用户界面中复杂的逻辑,数据和同步的问题,从而使得用户界面变得简单明了。根据这个类承担责任的多少,决定了表现设计模式的类型,可能是 SC , PV , PM 等,也就是说,这个类的成熟度决定了它将是那种设计模式。

PresenterHowMuch

有用的缩写

缩写 完整形式
V 视图 (View) 或者用户界面 (UI)
P 包含界面逻辑的表现类 (Presenter class which has the UI logic.)
L 用户界面逻辑
S 用户界面的状态
M 业务组件或业务对象
SC 监视控制器 (Supervising controller)
PV 被动视图 (Passive view)
PM 表现模型 (Passive view)

监视控制器模式 (SC)

  • 状态在视图中保存
  • 表现类拥有复杂的表现逻辑,只 关注简单的界面绑定逻辑,例如 WPF 或 Silverlight 等提供的绑定机制 (Presenter owns the complex presentation logic. Simple UI binding logic is taken care by using binding technologies like WPF binding and Silverlight binding. Anything complex is taken care presenter class. )
  • 表现类关注视图
  • 视 图不关注表现类
  • 视图通过数据绑定和业务模型进行关联

SC

被动视图模式 (PV)

  • 状态在视图中保存
  • 所有的界面逻辑都被包含在表现类中
  • 视图和业务模型完全独立,这种情 况下需要一些在业务模型和视图之间进行同步数据的工作
  • 表现类关注视图
  • 视图不关注表现类

PV

表现模型 (PM)

  • 表 现类包含逻辑
  • 表现类包含状态
  • 表现类代表抽象的用户界面
  • 表现类不关 注用户界面
  • 视图关注表现类
  • 视图与业务模型完全隔离

PM

MVVM

  • 继承 自表现模型
  • 使用 WPF 以及 Silverlight 的绑定机制

MVVM

MVC

  • 没 有表现类,有控制器 (Controller)
  • 请求首先到达控制器
  • 控制器负责绑定视图与业务模 型
  • 逻辑存在于控制器中

MVC

总结与对比

下表是这几种表现模 式从状态,逻辑与同步的角度进行的对比

状态 逻辑 同步
Supervising controller
表现类 X X
视图 X
业务模型 视图和业 务模型之间通过绑定进行连接。
Passive View
表现类 X X
视图 X
Presenter model
表现类 X X
视图 X
MVVM
表现类 X X
视图 X
使用 WPF 、Silverlight 的数据绑定机制
MVC
控制器 X X
视图 X

再来一个图的对比

theBiggerPicture

[转载]Asp.net MVC2 使用经验,性能优化建议

mikel阅读(917)

[转载]Asp.net MVC2 使用经验,性能优化建议 – Otis’s Technology Space – 博客园.

这个月一直在用 ASP.NET MVC2 做http://www.86e0.com/t 这个网站,用的时候是 aps.net MVC2 RC2,然后现在ASP.NET MVC2正式版已经是发布了。 在MVC的使用上,有一些心得。下面作一下总结,希望对大家有用,也欢迎大家讨论。

1.关于 缓存

缓存上,数据层上的缓存是必须的,这点不必多说了。

另一个很重要的是:视图片段缓存。

我参考了老赵的写的三篇关于片段缓存的文章:

本想用老赵的了,但是我发现Asp.net MVC2 的有一个新功能: Html.Partial可以返回生成的HTML, 返回的类型是:MvcHtmlString. 虽然要利用Partial View才能生成Html片段,但是我想这个已经够我用的了, 所以我做了一个这样一个Helper,主要是将生成的HTML片段缓存到Memcached里。代码如下:

01 public static class MvcHtmlHelper
02 {
03 public static MvcHtmlString MemcacheHtmlPartial(this HtmlHelper htmlHelper,int duration, string partialViewName, object model, ViewDataDictionary viewData)
04 {
05 object obaear = htmlHelper.ViewContext.RouteData.DataTokens["area"];
06 string area=string.Empty;
07 if (obaear != null) area = obaear.ToString();
08 string key = string.Format("MemcacheHtmlPartial_{0}{1}", area, partialViewName);
09 object ob = DistCache.Get(key);
10 if (ob == null)
11 {
12 MvcHtmlString mstr = htmlHelper.Partial(partialViewName, model, viewData);
13 DistCache.Add(key, mstr.ToString(), TimeSpan.FromSeconds(duration));
14 return mstr;
15 }
16 else
17 {
18 return MvcHtmlString.Create((string)ob);
19 }
20 }
21 }

然后,我觉得,这样,在每次请求时,还是要在Controller 里把数据取出来,然后再传到 Partial View里。 既然已经缓存了,就应该不用每次请求都要在Controller里把数据取出来才对!虽然数据层会有缓存。

所以我,能不能再省下去Controller取数据的消耗,于是又有了以下代码,其功能是:缓存Action生成的HTML到Memcached 里。

01 public static MvcHtmlString MemcacheHtmlRenderAction(this HtmlHelper htmlHelper, int duration, string actionName,string controllerName, RouteValueDictionary routeValues)
02 {
03 object obaear = htmlHelper.ViewContext.RouteData.DataTokens["area"];
04 string area = string.Empty;
05 if (obaear != null) area = obaear.ToString();
06 string key = string.Format("MemcacheHtmlRenderAction_{0}{1}{2}", area, controllerName,actionName);
07 object ob = DistCache.Get(key);
08 if (ob == null)
09 {
10
11 // htmlHelper.RenderAction(actionName, controllerName, routeValues);
12 StringWriter writer = new StringWriter(CultureInfo.CurrentCulture);
13 ActionHelper(htmlHelper, actionName, controllerName, routeValues, writer);
14 string wStr = writer.ToString();
15 DistCache.Add(key, wStr,TimeSpan.FromSeconds(duration));
16 MvcHtmlString mstr = MvcHtmlString.Create(wStr);
17
18 return mstr;
19 }
20 else { return MvcHtmlString.Create((string)ob); }
21 }

说明一下,Actionhelper的方法是在MVC原代码里提取出来的。 因为MVC2里的 Html.RenderAction方法并没有返回 MvcHtmlString的重载版。那位有更好的方法?

其实,MVC里的Action有输出缓存,所以直接在View里用 Html.RenderAction都可以解决很多问题了。这个主要是可以用程序管理缓存。

2.关于静态内容的放置

习惯上,静态内容会放在 mvc程序所在的目录下,比如说js,css,上传的图片等。但是这样的话,所有的静态请求都要经过 aspnet_isapi 处理,这样是非常不合算的。所以静态内容一般都会放在另外的子域上。http://www.86e0.com/t 是放在 cdn.86e0.com上。

3.关于强类型ViewModel

我基本上看了老赵的ASP.NET MVC最佳实践。 其中有一点,就是强烈推荐使用强类型的ViewModel. 我试了一些页面,发现用强类型的ViewModel,现阶段并不适用于我。因为我是用NbearLite,从数据库抓出来的大多是DataTable. 我是觉得DataTable+NbearLite蛮方便的,虽然没有动态语言的数据访问来得方便,但是比用Entity,ViewModel, DTO,等等来说,还是可以省下很多代码。然后,最重要的是,由于我这种站经常会修改,所以数据库改变,加字段,减字段是很经常性的事。但是,用 NbearLite + DataSet,DataTable,却非常方便。

所以我觉得,做Asp.net MVC,如果你不是用DDD,DDT的话,用DataTable还是可以的。因为DDD,DDT学习起来还是要点成本的。

4.关于URL生成

URL生成, 老赵写了一系列文章:

  • 各 种URL生成方式的性能对比
  • 各 种URL生成方式的性能对比(结论及分析)
  • 为 URL生成设计流畅接口(Fluent Interface)
  • URL生成方式性能优化结果我直接选择

    Raw方式了, 速度最快的,才是适合我的。呵。 而不是强类型的才是适合我的。

    最后,分享一个很实用的ASP.NET MVC 分页Helper.

    这个Helper引自重典老大的blog:http://www.cnblogs.com/chsword/ . 我在之前做了少少修改,现已经在http://www.86e0.com/t 上使用了。

    效果如下:

    image

    请大家注意生成的 URL, 是用 ?参数=页码 的方式。代码如下:

    01 /// <summary>
    02 /// 分页Pager显示
    03 /// </summary>
    04 /// <param name="html"></param>
    05 /// <param name="currentPageStr">标识当前页码的QueryStringKey</param>
    06 /// <param name="pageSize">每页显示</param>
    07 /// <param name="totalCount">总数据量</param>
    08 /// <returns></returns>
    09 public static string Pager(this HtmlHelper html, string currentPageStr, int pageSize, int totalCount)
    10 {
    11 var queryString = html.ViewContext.HttpContext.Request.QueryString;
    12 int currentPage = 1; //当前页
    13 if(!int.TryParse(queryString[currentPageStr], out currentPage)) currentPage = 1; //与相应的QueryString绑定
    14 var totalPages = Math.Max((totalCount + pageSize - 1) / pageSize, 1); //总页数
    15 var dict = new RouteValueDictionary(html.ViewContext.RouteData.Values);
    16
    17 var output = new StringBuilder();
    18
    19 foreach (string key in queryString.Keys)
    20 if (queryString[key] != null && !string.IsNullOrEmpty(key))
    21 dict[key] = queryString[key];
    22 if (totalPages > 1)
    23 {
    24 if (currentPage != 1)
    25 {//处理首页连接
    26 dict[currentPageStr] = 1;
    27 output.AppendFormat("<span class=\"p_home\">{0}</span>", html.RouteLink(" 首页", dict));
    28 }
    29 if (currentPage > 1)
    30 {//处理上一页的连接
    31 dict[currentPageStr] = currentPage - 1;
    32 output.AppendFormat("<span class=\"p_up\">{0}</span>", html.RouteLink(" 上一页", dict));
    33 }
    34 else
    35 {
    36 output.AppendFormat("<span class=\"p_disable\">{0}</span>","上一页");
    37 }
    38 int currint = 5;
    39 for (int i = 0; i <= 10; i++)
    40 {//一共最多显示10个页 码,前面5个,后面5个
    41 if ((currentPage + i - currint) >= 1 && (currentPage + i - currint) <= totalPages)
    42 if (currint == i)
    43 {//当前页处理
    44 output.Append(string.Format("<span class=\"p_current\">{0}</span>", currentPage));
    45 }
    46 else
    47 {//一般页处理
    48 dict[currentPageStr] = currentPage + i - currint;
    49 output.AppendFormat("<span class=\"p_num\">{0}</span>",html.RouteLink((currentPage + i - currint).ToString(), dict));
    50 }
    51 }
    52 if (currentPage < totalPages)
    53 {//处理下一页的链接
    54 dict[currentPageStr] = currentPage + 1;
    55 output.AppendFormat("<span class=\"p_down\">{0}</span>", html.RouteLink(" 下一页", dict));
    56 }
    57 else
    58 {
    59 output.AppendFormat("<span class=\"p_disable\">{0}</span>", "下一页");
    60 }
    61 if (currentPage != totalPages)
    62 {
    63 dict[currentPageStr] = totalPages;
    64 output.AppendFormat("<span class=\"p_last\">{0}</span>",html.RouteLink(" 末页", dict));
    65 }
    66 }
    67 output.AppendFormat("<span class=\"p_count\">第{0}页/共{1}页</span>", currentPage, totalPages);//这个统计加不加都行
    68 return output.ToString();
    69 }

    另: http://www.86e0.com/t 是做淘宝客类应用的。园子里还有谁在做淘宝客类网站么? 有的话多交流。^_^

  • [转载]利用 C# 去执行 Log Parser - 心之镇 - 博客园

    mikel阅读(1427)

    [转载]利用 C# 去执行 Log Parser – 心之镇 – 博客园.

    通过 .NET Framework 的 COM interop (COM 交互操作)特性,可以很方便地在 .NET 应用程序中使用 Log Parser,.NET Framework 的 COM interop 是通过 Runtime Callable Wrappers (RCW) 来实现对 COM 的操作的,RCW 是 .NET 中的一个类。

    编译环境:VS2005 C#

    首先在你新建的项目中添加引用:LogParser.dll,那么出现在你解决方案的引用栏中:MSUtil。你可以在对象浏览器中查到此库的 各种类型接口。
    接下来要在你的cs文件的开头,引用你想调用的类型,例如:
    using LogQuery = MSUtil.LogQueryClassClass;
    using LogRecordSet = MSUtil.ILogRecordset;
    using LogRecord = MSUtil.ILogRecord;
    using W3CLogInputFormat = MSUtil.COMW3CInputContextClassClass;
    using CSVLogOutputFormat = MSUtil.COMCSVOutputContextClassClass;

    举个实例:用 Log Parser 和 C# 打造网站访问统计分析系统,就是用 C# 调用 Log Parser,利用 Log Parser 去分析 IIS 的日志文件。我们这里的日志文件是 W3C 格式的。

    我们建立一个 .cs 文件,引入:

    using LogQuery = Interop.MSUtil.LogQueryClassClass;
    using IISInputFormat = Interop.MSUtil.COMIISW3CInputContextClassClass;
    using LogRecordSet = Interop.MSUtil.ILogRecordset;

    类中关键代码 如下:

    LogQuery oLogQuery = new LogQuery();
    IISInputFormat oIISInputFormat = new IISInputFormat();
    string query = @”SELECT COUNT(DISTINCT c-ip) AS hits
    FROM ‘C:\WINDOWS\system32\Logfiles\W3SVC1\ex070820.log’
    WHERE cs-uri-stem like ‘%.asp’
    AND sc-status=200″;
    LogRecordSet oRecordSet = oLogQuery.Execute(query, oIISInputFormat);
    if (!oRecordSet.atEnd())
    {
    hits = (int)oRecordSet.getRecord().getValue(“hits”);
    }
    oRecordSet.close();

    看得出来跟使 用数据库没有多大区别,查询语句中 c-ip、cs-uri-stem、sc-status 等都是日志文件的字段,直接打开日志文件可以找到这些内容。

    通过C#与 LogParser的结合可以极大限度的发挥LogParsr的功能。让它不仅仅局限与命令行的输入。。。

    [转载]Log Parser使用教程

    mikel阅读(1963)

    Log Parser

    Log Parser 是一種輕巧好用的公用程式,名符其實,它可以快速簡單地剖析純文字記錄檔。而且 Log Parser 也可以用來快速簡單地剖析事件記錄、檔案系統、登錄,甚至是 Active Directory。
    像是對IIS的log file進行類似SQL語法的分析查詢,將結果顯示於螢幕、檔案或SQL數據庫內。

    Log Parser本身是個命令字元工具(command-line tool),使用時必須先進入命令提示字元,並切換至Log Parser的安裝目錄,然後輸入LogParser指令即可使用。

    LogParser -h GRAMMAR : SQL Language Grammar 查詢語言的語法

    LogParser -h FUNCTIONS [ ] : Functions Syntax 函數語法

    LogParser -h EXAMPLES : Example queries and commands 範例

    輸出入格式

    -i: : one of IISW3C, NCSA, IIS, IISODBC, BIN, IISMSID, HTTPERR, URLSCAN, CSV, TSV, W3C, XML, EVT, ETW, NETMON, REG, ADS, TEXTLINE, TEXTWORD, FS, COM (if omitted, will guess from the FROM clause)

    -o: : one of CSV, TSV, XML, DATAGRID, CHART, SYSLOG, NEUROVIEW, NAT, W3C, IIS, SQL, TPL, NULL (if omitted, will guess from the INTO clause)

    示範:
    1. 2006/4/10當日IIS網站一天的擊點率(Hit Rate)

    logparser “select count(*) from ex.log”
    2. 以圖形化資料網格方式輸出

    logparser “SELECT count(*) as [HitRate] INTO datagird FROM ex060410.log”

    或是在INTO後面改為其它檔案格式:your_output_name.csv, Report.xml, MyChart.gif

    LogParser “SELECT TOP 20 cs-uri-stem, COUNT(*) AS Hits INTO MyChart.gif FROM ex060410.log GROUP BY cs-uri-stem ORDER BY Hits DESC” -chartType:Column3D -groupSize:1024×768
    3. 統計每一網頁資源的擊點率

    logparser “SELECT cs-uri-stem as [Web Page], count(*) as [HitRate] INTO datagird FROM ex.log group by cs-uri-stem”
    4. 輸出至Html

    logparser “SELECT cs-uri-stem as [Web Page], count(*) as [HitRate] INTO datagird FROM ex.log group by cs-uri-stem order by count(*) desc” -o:TPL -tep:HTML_temp.txt
    5. 查看登錄器並輸出至SQL Server存成資料表

    LogParser “SELECT * INTO MyTable FROM \HKLM” -i:REG -o:SQL -server:MyServer -database:MyDatabase -driver:”SQL Server” -username:TestSQLUser -password:TestSQLPassword -createTable:ON
    6. 搜尋電腦中前10大檔案

    LogParser “SELECT TOP 10 * FROM C:\*.* ORDER BY Size DESC” -i:FS
    7. 將指令列執行結果分析再輸出,例分析netstat結果

    netstat | LogParser “SELECT * FROM STDIN” -i:TSV -iSeparator:space -nSep:2 -fixedSep:off -nSkipLines:3

    備註:

    IIS 記錄檔預設位置C:\WINDOWS\system32\LogFiles\W3SVC1\exyymmdd.log,可由「網際網路資訊服務(IIS) 管理員」/網站/預設的網站/右鍵「內容」/「網站」Tab/「啟用記錄」的「內容」來設定。

    「一般」Tab可設定儲存Log的位置及排程。

    「進階」Tab可設定記錄的資訊選項,例:用戶端IP位址(c-ip)、傳送方法(cs-method) 、URI主體(cs-uri-stem) 、URI查詢(cs-uri-query)、通訊協定狀態(sc-status)、花費時間(time-taken)。

    參考:
    * Windows 2003 Server 電子雜誌 2005年10月份
    * 如果您還不熟悉 Log Parser 2.2,請您參閱《指令碼的物語》(英文) 專欄-Tales from the Script
    * Microsoft TechNet Log Parser 2.2
    * 尋找免費的 Log Analyser 工具?Internet Information Services (IIS) 6.0 Resource Kit Tools

    應用範例:
    * LogParser -h EXAMPLES 裡面有很多呦!
    * 嗨,Scripting Guy!我要如何找出電腦中前 20 大的檔案呢?

    [转载]用 LogParser 將 IIS Log 匯入SQL Server 2005 - 瓶水相逢.Net- 點部落

    mikel阅读(940)

    [转载]用 LogParser 將 IIS Log 匯入SQL Server 2005 – 瓶水相逢.Net- 點部落.

    指令:

    C:\Program Files\Log Parser 2.2\LogParser.exe ” “SELECT TO_LOCALTIME(TO_TIMESTAMP([date], [time])), [s-sitename], [s-computername], [s-ip], [cs-method], [cs-uri-stem], [cs-uri-query], [s-port], [cs-username], [c-ip], [cs-version], [cs(User-Agent)], [cs(Cookie)], [cs(Referer)], [cs-host], [sc-status], [sc-substatus], [sc-win32-status], [sc-bytes], [cs-bytes], [time-taken] INTO IISLog FROM C:\WINDOWS\system32\LogFiles\W3SVC1\ex*.log WHERE TO_LOWERCASE (EXTRACT_EXTENSION(cs-uri-stem)) NOT IN (‘gif’;’jpg’;’png’;’bmp’;’ico’;’axd’)” -o:SQL -server:192.168.x.x -database:MyDB -driver:”SQL Server” -username:uname -password:passwd-createTable:OFF

    藍色 的地方,利用 TO_TIMESTAMP 函數將 Date 與 Time 合併成一個 Datatime 型態的欄位。再利用 TO_LOCALTIME 函數,將 IIS Log 內的 UTC 時間,轉換成本地時間 (GMT +8)。

    紅色 的地方,依照自己的設定環境加以更換。

    [转载]LogParser 日期相關心得筆記與分析 IIS Logs 技巧分享

    mikel阅读(1127)

    [转载]LogParser 日期相關心得筆記與分析 IIS Logs 技巧分享.

    最近工作上又有機會用到 LogParser 來分析 IIS 紀錄,藉此整理一下 LogParser 對時間、日期處理的各種使用情境,以及在分析 IIS Log 時的一些小技巧。

    與日期時間相關的操作方法

    取得系統時間 ( GMT +0000 )

    SYSTEM_TIMESTAMP()

    取得系統本地時間 ( 若在台灣就會回傳 GMT +0800 的時間 )

    TO_LOCALTIME(SYSTEM_TIMESTAMP())

    取得 GMT +1000 的時間

    ADD(SYSTEM_TIMESTAMP(), TIMESTAMP('10', 'h'))

    取得今天的日期時間字串 (與 .NET 的 DateTime.ToString 方法 類似)

    TO_STRING(TO_LOCALTIME(SYSTEM_TIMESTAMP()), 'yyyy-MM-dd HH:mm:ss')

    取得今天的日期字串 (與 .NET 的 DateTime.ToString 方法 類似)

    TO_STRING(TO_LOCALTIME(SYSTEM_TIMESTAMP()), 'yyyy-MM-dd')

    顯示昨天的日期時間

    請注意下例的 TIMESTAMP 函示,由於所有 Timestamp 是從 0000-01-01 00:00:00 開始計算的(代表0),所以當第一個參數是傳入 2,第二個參數是 d (日期),則代表 0000-01-02 00:00:00 這個時間點,所以這裡所換算出來的 Timestamp 數值代表 1 天的時間,然後再利用 SUB 相減函示與本地時間計算後,就是昨天的時間了!

    SUB(TO_LOCALTIME(SYSTEM_TIMESTAMP()), TIMESTAMP('2', 'd'))

    顯示明天的日期時間

    ADD(TO_LOCALTIME(SYSTEM_TIMESTAMP()), TIMESTAMP('2', 'd'))

    取得本月第一天的開始時間

    SUB(
        TO_LOCALTIME(SYSTEM_TIMESTAMP()), 
        TO_TIMESTAMP(TO_STRING(TO_LOCALTIME(SYSTEM_TIMESTAMP()), 'dd HH:mm:ss'), 'dd HH:mm:ss')
       )

    注意事項

    • 如果你希望用「本地時間」計算日期部分,請不要用 SYSTEM_DATE(),即便你用範例所提供的 TO_LOCALTIME( SYSTEM_DATE() ) 我自己實驗的結果騎日期一樣是錯的,並非系統本地日期!

    與 IIS 相關的技巧

    由於 IIS Log 的日期、時間是分開兩個欄位,必須用以下語法合併

    TO_TIMESTAMP([date], [time])

    由於預設 IIS Log 都是以 GMT 標準時間當成 Log 的紀錄,若要轉換成本地時間可用以下語法:

    TO_LOCALTIME(TO_TIMESTAMP([date], [time]))

    篩選出特定時間區間的 Log 紀錄

    SELECT
        *
    FROM
        C:\inetpub\logs\LogFiles\W3SVC1\*.log
    WHERE
        TO_TIMESTAMP([date], [time])
        BETWEEN TO_TIMESTAMP('2009-11-13 00:00:00', 'yyyy-MM-dd HH:mm:ss')
        AND     TO_TIMESTAMP('2009-11-13 23:59:59', 'yyyy-MM-dd HH:mm:ss')

    讀取 IIS Log 可以不用明確指定路徑,如果要分析本機的 IIS Log 可以直接指定 [站台識別元] ( Site ID ) 或 [站台名稱] 即可載入該站台下所有 Logs  ( 可用 * 萬用字元 )

    如下範例為載入 Site ID 為 1 與 2232 以及站台名稱為 VWeb 開頭的所有站台的所有 Logs

    SELECT * FROM <1>, <2232>, <www.*.com.tw>

    在 IIS 7 / IIS 7.5 如果要用這種簡易的語法必須要安裝 [IIS 6 Metabase 相容性] 才能使用:

    網頁伺服器 (IIS)  :: 新增角色服務 :: 管理工具 :: IIS 6 管理相容性 :: IIS 6 Metabase 相容性

    Windows Features: IIS Metabase and IIS 6 configuration  compatibility