[教程]解读ASP.NET Portal Starter Kit(1)——数据库篇

mikel阅读(665)

  ASP.NET Portal Starter Kit数 据库结构总体上来讲是由网站引擎的核心表(用户表、角色表和角色关系表)和各个用户模块相关的表组成。核心表存储整个网站的用户权限的配置信息(详细的用 法及说明将放到《角色身份认证篇》中讲)。各用户模块存储各个功能模块的信息。各个用户功能模块表都是独立的。这样有利于新增模块扩展功能。同时也可将各 个功能模块的表分别部署到不同的数据库中提高程序的高伸缩性和可扩展性。数据的访问方式是全部通过存储过程进行的。这样做的好处有:1、提高了数据库的性能;2、杜绝了SQL注入式攻击(在我的理解上应该是);3、可将可将连接到SQL Server数据库用户的权限只配置成只能对指定存储过程进行执行操作,这样就进一步提高了数据库的安全性。

数据库中的表:

Portal_Announcements公告信息表(在公告信息模块中用)

字段名

类型

含义

备注

ItemID

Int

链接Id

主键(自动编号)

ModuleID

Int

所属模块ID

决定该公告在那个模块中显示(模块ID:用户配置文件PortalCfg.xml中的模块表ID,以下皆同)

CreatedByUser

Nvarchar(100)

创建者

记录创建和修改该公告的用户

CreatedDate

Datetime

创建时间

记录创建和修改该公告的时间

Title

Nvarchar(100)

名称

 

MoreLink

Nvarchar(150)

更多细节的链接

 

MobileMoreLink

Nvarchar(150)

移动细节链接地址

在“移动设备浏览器”上的更多细节链接地址

ExpireDate

Datetime

有效日期

 

Description

Nvarchar(2000)

描述

 

 

Portal_Contacts联系方式信息表(在联系方式模块中用)

字段名

类型

含义

备注

ItemID

Int

链接Id

主键(自动编号)

ModuleID

Int

所属模块ID

决定该联系方式在那个模块中显示

CreatedByUser

Nvarchar(100)

创建者

记录创建和修改该联系方式的用户

CreatedDate

Datetime

创建时间

记录创建和修改该联系方式的时间

Name

Nvarchar(50)

联系人姓名

 

Role

Nvarchar(100)

联系人角色

与系统框架中的角色不同,理解成职位更合适

Email

Nvarchar(100)

联系人Email

 

Contact1

Nvarchar(250)

联系方式1

 

Contact2

Nvarchar(250)

联系方式2

 

 

Portal_Discussion用户讨论留言信息表(用户讨论模块用)

字段名

类型

含义

备注

ItemID

Int

链接Id

主键(自动编号)

ModuleID

Int

所属模块ID

决定该讨论在那个模块中显示

Title

Nvarchar(50)

讨论标题

 

CreatedDate

Datetime

创建时间

记录创建该讨论的时间

Body

Nvarchar(100)

留言内容

 

CreatedByUser

Nvarchar(100)

创建者

记录创建和修改该讨论的用户

DisplayOrder

Nvarchar(100)

显示顺序

记录回复的讨论的时间和本身提交的时间 (可用作讨论留言的排序和显示回复关系)

 

Portal_Documents用户上传文档信息表(在显示文档信息模块中用)

字段名

类型

含义

备注

ItemID

Int

链接Id

主键(自动编号)

ModuleID

Int

所属模块ID

决定该联系方式在那个模块中显示

CreatedByUser

Nvarchar(100)

创建者

记录创建和修改该联系方式的用户

CreatedDate

Datetime

创建时间

记录创建和修改该联系方式的时间

FileNameUrl

Nvarchar(250)

 

上传至服务器中的文件路径

FileFriendlyName

Nvarchar(150)

文档名称

友好的文件名称显示 

Category

Nvarchar(50)

类别

 

Content

Image

内容(二进制)

上传到数据库中的内容

ContentType

Nvarchar(50)

类型

上传文件的类型

ContentSize

Int

大小

上传文件的大小

 

Portal_Events事件信息表(在显示事件信息的模块中使用)

字段名

类型

含义

备注

ItemID

Int

链接Id

主键(自动编号)

ModuleID

Int

所属模块ID

决定该事件在那个模块中显示

CreatedByUser

Nvarchar(100)

创建者

记录创建和修改该事件的用户

CreatedDate

Datetime

创建时间

记录创建和修改该事件的时间

Title

Nvarchar(100)

事件名称

 

WhereWhen

Nvarchar(150)

发生地点

 

Description

Nvarchar(2000)

描述

 

ExpireDate

Datetime

有效日期

 

 

Portal_HtmlText静态HTML信息表(在显示静态静态HTML的模块中用,可用于显示新闻等文本)

字段名

类型

含义

备注

ModuleID

Int

所属模块ID

决定该静态HTML在那个模块中显示(主键,限定一个模块只对应一个静态HTML文件)

DesktopHtml

Ntext

桌面HTML内容

 

MobileSummary

Ntext

移动摘要

在“移动设备浏览器”上显示的摘要

MobileDetails

Ntext

移动细节

在“移动设备浏览器”上显示的细节

 

Portal_Links链接信息表(快速链接模块和连接模块用)

字段名

类型

含义

备注

ItemID

Int

链接Id

主键(自动编号)

ModuleID

Int

所属模块ID

决定该链接在那个模块中显示

CreatedByUser

Nvarchar(100)

创建者

记录创建和修改该链接的用户

CreatedDate

Datetime

创建时间

记录创建和修改该链接的时间

Title

Nvarchar(100)

名称

 

Url

Nvarchar(250)

链接地址

 

MobileUrl

Nvarchar(250)

移动链接地址

 

ViewOrder

Int

排序号

 

Description

Nvarchar(2000)

描述

 

 

Portal_Roles角色信息表(门户网站引擎核心表)

字段名

类型

含义

备注

RoleID

Int

角色Id

主键(自动编号)

PortalID

Int

门户网址ID

可架设多个门户站点而共用一个数据库,通过PortalID区分

RoleName

Nvarchar(50)

角色名称

 

Portal_UserRoles用户角色关系表(门户网站引擎核心表)

字段名

类型

含义

备注

UserID

Int

用户Id

关联用户信息表(Portal_Roles

RoleID

Int

角色Id

管理角色信息表(Portal_Users

 

Portal_Users用户信息表(门户网站引擎核心表)

字段名

类型

含义

备注

UserID

Int

用户Id

主键(自动编号)

Name

Nvarchar(50)

用户姓名

 

Password

Nvarchar(50)

密码

采用MD5的加密方式存储

Email

Nvarchar(100)

用户Email

用于登录,并设置成唯一性索引(可防止注册相同的Email,这样设置后当有相同的Email插入时程序就会抛出异常,捕获这个异常就可判断Email是否重复,这样就可以省掉判断Email是否重复的代码

 

数据库中的存储过程:

存储过程名称

说明

Portal_AddAnnouncement

添加新公告

Portal_AddContact

添加新联系方式

Portal_AddEvent

添加新事件

Portal_AddLink

添加新链接

Portal_AddMessage

新建一条新的讨论留言,其中@ParentID的参数为被回复留言的Id,通过该ID找到该留言的DisplayOrder,加上新增留言的时间就是新留言的DisplayOrder

Portal_AddRole

添加角色信息

Portal_AddUser

添加一个新用户,返回用户的Id

Portal_AddUserRole

添加用户角色关系

Portal_DeleteAnnouncement

删除公告信息(注:以下删除部分若无特殊说明均为删除指定ItemID的信息)

Portal_DeleteContact

删除联系方式

Portal_DeleteDocument

删除用户上传文档

Portal_DeleteEvent

删除事件信息

Portal_DeleteLink

删除链接信息

Portal_DeleteModule

当删除一个模块时,联动的删除该模块相关的全部信息

Portal_DeleteRole

删除角色信息

Portal_DeleteUser

删除用户

Portal_DeleteUserRole

删除角色用户关系

Portal_GetAnnouncements

根据ModuleID(模块ID)返回有效期内的公告信息

Portal_GetAuthRoles

像是没有用到该存储过程,而且该存储过程涉及的表数据库中没有(在用户配置文件中有类似的表),需要在深入研究

Portal_GetContacts

根据ModuleID(模块ID)返回联系方式

Portal_GetDocumentContent

根据文档的(ItemID)获取存储在数据库中的文档信息

Portal_GetDocuments

根据ModuleID(模块ID)返回用户上传文档信息

Portal_GetEvents

根据ModuleID(模块ID)返回有效期内的事件信息

Portal_GetHtmlText

根据ModuleID(模块ID)返回静态HTML文本信息

Portal_GetLinks

根据ModuleID(模块ID)返回连接信息,并按(ViewOrder)排序号排序

Portal_GetNextMessageID

获取讨论的下一条留言

Portal_GetPortalRoles

获取指定门户站点(指定PortalID)的全部角色信息

Portal_GetPrevMessageID

获取讨论的上一条留言

Portal_GetRoleMembership

根据角色ID获取该角色对应的用户成员信息

Portal_GetRolesByUser

根据用户Email获取用户角色信息

Portal_GetSingleAnnouncement

根据公告的(ItemID),获取单个公告的信息

Portal_GetSingleContact

根据联系方式的(ItemID),获取单个联系方式的信息

Portal_GetSingleDocument

根据文档的(ItemID),获取单个文档的信息

Portal_GetSingleEvent

根据事件的(ItemID),获取单个事件的信息

Portal_GetSingleLink

根据链接的(ItemID),获取单个链接的信息

Portal_GetSingleMessage

根据留言的(ItemID),获取单个留言的信息

Portal_GetSingleRole

根据角色ID,获取单个角色的信息

Portal_GetSingleUser

根据用户Email,获取单个用户的信息

Portal_GetThreadMessages

根据父留言信息的DisplayOrder,返回按时间顺序和回复关系返回子留言信息

Portal_GetTopLevelMessages

根据ModuleID(模块ID)返回顶层留言信息

Portal_GetUsers

获取全部的用户信息,并按Email排序

Portal_UpdateAnnouncement

更新公告信息

Portal_UpdateContact

更新联系方式信息

Portal_UpdateDocument

更新文档信息,当未找到指定文档ID时添加新的文档信息

Portal_UpdateEvent

更新事件信息

Portal_UpdateHtmlText

更新静态HTML文本信息

Portal_UpdateLink

更新指定的连接信息

Portal_UpdateRole

更新角色信息

Portal_UpdateUser

更新用户信息

Portal_UserLogin

根据emailpassword返回登录的用户姓名(用于判断用户登录是否通过)

  
更多相关内容:点击这里>>

[教程]Portal应用点滴(一) —— Portal简介

mikel阅读(999)

  [引言] 早在2003年底,Portal这个名词第一次出现在了我的技术辞典中。当时对Portal的理解仅仅限于“页面上的几个可以随意摆放的小窗口”,觉得没 有什么特别的,也没有把这项技术放在心上了。2004年底,我参与了一个基于Portal的项目开发。在过去将近一年的时间里,一直都在和IBM WebShpere Portal打交道,这给了我认识并学习Portal的好机会。Daily Portal这一系列的文章算是我这一年来对Portal认识的小小一个总结吧。
 
        Portal是一个内涵很丰富的名词,其中文意思是“大门,正门,入口或者进入的手段”。而这项技术也确实人如其名,它为用户提供了一种
简单而且统一的访问Web Application的入口。在这个定义中,统一是最为重要的方面,因为在一个企业中,其IT Support可能是由很多很多相对独立的系统组成,而它们都有各自的访问入口。在没有使用Portal技术之前,不管是企业内部员工,企业的合作伙伴以及企业用户都不得不在几个系统中来回跳转以获得对自己有用的信息,对效率有很大的影响。更糟糕的是,跨系统对信息进行采集和总括的时候,散布在各个系统的相关信息将会以不同的呈现方式出现在用户面前,要迅速从中获得所需要的信息集将是一项十分艰巨的任务。而Portal的统一意味着集成(integrate),它所提供的不仅仅是将几个系统的信息采集过来在一张页面上显示出来,还要提供安全,权限,统一的登陆服务,个性化设置等等。利用Portal来进行Legacy系统的整合是一个十分重要而且有意义的课题,可惜这一年来所做的项目仅仅是对一个系统升级,还没有机会亲身体验Portal在系统整合方面的强大优势,希望有经验的朋友可以不吝赐教。
        说完Portal的定义,我们来看一下Portal的组成结构:

portal.gif

从图中我们可以看出,Horizontal Portals Infrastructure部分为建立不同的Portal系统提供了最基础的服务,通常它由以下几个方面组成:
        1、Presentation:提供了在不同的client端显示的服务;
        2、Personalization:基于User Profile的个性化设置服务;
        3、Collaboration:提供与其他系统,如果Mail,IM等的协作服务;
        4、Portlets:这个名词是不是很熟悉呢?Portlet提供了访问Web Application的服务;
        5、Search and Workflow:提供与Legacy系统集成的服务;
        6、Publish and subscribe:提供内容发布与订阅服务;
        7、Administration and Security: 管理及安全服务;
        8、Integration:提供与数据库连接以及元数据共享服务。

而在这八个方面当中,Portlets是最为核心的,通常我们对Portal的第一步感性认识也是从Portlet开始。简单的说,从页面上看到的一 个个小窗口就是portlet。我们可以把Portlet看成是Web世界中的可重用的Component,它为用户提供了访问Web Application,Web Service或者Web Content的服务。一个Portal Page通常会包含几个Portlet,而这些Portlet之间是相互独立且互不影响的,也就是当一个Portlet无法正常工作的时候,另外的 Portlet并不会因此而罢工,仍会正常地进行处理。
        最后大致说一下,使用Portal技术会给企业带来怎样的益处。首先,以 可重用的Portlet作为核心的开发方式使得企业可以更快速的响应商业上的需求;Portal提供了强大的系统集成功能,降低了系统维护以及使用的成 本,同时也降低了员工培训的成本;Portal提供了统一的访问入口使得用户访问各个系统的时候有更好的用户体验以及更好的安全性。
 

在昨天的Post中,偶已经对Portal进行了简单的介绍,不知道各位对Portal是不是有一点点认识了呢?如果还是云里雾里的话也没有关系,毕竟Portal所涵盖的内容是很广博的,掌握它需要时间,了解也是需要时间的。在初步介绍了Portal的概念之后,今天我们来探讨一下Portal的核心——Portlet。
        首先,Portlet在表现形式上就是我们所看到的页面上一个个小窗口,但是它绝不仅限于将Web Content以模块化的方式显示出来。实际
上,每一个Portlet就是一个完整的Web Application,拥有完整的MVC。而Portlet的开发,部署,管理和显示都是完全独立的,不受其他Portlet的影响,从而其functionality(功能性)也是独立的,也就是说一个Portlet就是一个处理特定义务逻辑的Component。正是Portlet这种独立性与功能性,使得Portal的Administrator或者是End User能够很方便地配置符合自己要求的页面,充分提高信息采集与概括的效率。
        做完了表面功夫之后,我们将注意力集中到Portlet的底层实现中来。从Portlet的类继承关系上,我们可以看到Portlet的
Superclass仍然是HttpServlet。事实上,虽然Portlet相对Servlet而言进行了很大程度上的扩展并有很大的优越性,但是其本质与Servlet并无二致,都是一个处理HttpRequest并返回HttpResponse的过程,只是这个过程已经不是Servlet中的doGet、doPost或者service那么简单了,而是分成了两个方面:Event Phase和Content Rendering Phase。这就是Portlet的Two-phase Portlet Request Processing了。也许你会对这样的划分产生了兴趣,为什么需要这样去划分呢?其实,这样做是为了保持Portlet之间独立性。你可以设想一下,在一个Portal Page中包含了两个Portlet,而你在其中的一个Portlet输入了数据并提交,那么该Portlet将会处理你的请求并返回相应的结果,而另外一个Portlet则需要保持原来的样子。在这样的一个过程,对于处理了你的请求的Porlet,其Processing包含了两个phase,而另外一个Portlet则仅是进行了Content Rendering Phase的处理并将cache的内容返回。写到这里,我不禁想到了Ajax。以上的case,可以看作是页面的部分更新,如果使用了Ajax,是不是可以简化Portlet Request的处理过程呢?我相信这是一个有趣并值得深入探讨的问题。[1] 
        说完了比较底层的东西,我们最后再来看看Portlet比较特殊的一个地方:Portlet Mode。一个Portlet通常会包含几种display
mode,最常见的莫过于Minimize和Maxmize的display mode,除此之还会有Help Mode和Edit Mode。Help Mode为用户提供关于该Portlet的帮助信息,而Edit Mode则为用户提供了设置Portlet属性的界面。用户可以通过点击Portlet的title bar上的图标(图中黑色椭圆框住的部分)来切换不同的display mode,如下图所示:

PortalBar.bmp

        以上对Portlet作了简单的介绍,希望能够给各位以初步的印象。讲了那么多理论上的东西,大家是不是有点厌烦了呢?不要急,在接下来的Post中就要动手实践了,敬请关注哦! 🙂

 

在上一篇Post中,我提到要开始实践了,但是酝酿一篇与实践密切相关的Post还真的很费劲。毕竟建立一个portlet的方法有很多种,而且还会因使用的工具不同而方法迥异。平常在工作当中都是使用IBM提供的Portal Toolkit进行Portlet开发,打开Create New Portlet Wizard,跟着提示点几下按钮,一个漂亮的Portlet就在弹指之间完成了。如果我把这样的过程step by step地写下来,似乎意义不大,毕竟这些内容在IBM的红皮书中都写得清清楚楚了,况且由Portal Tookit搭建起来的开发环境也不是唾手可得的,看来还得从Open Source的东西入手了。
        在Apache的Portals项目中有一个名叫Pluto的子项目,它致力于构建一个简单的Portlet Container,让程序员可以Deploy并运行所开
发的Portlet。Pluto相对于BEA、IBM等大公司提供的Portal Server而言,尽管显得功能简陋,但是作为Portlet API的一个参考实现还是挺有权威的,其简单性也正好让我们更加关注Portlet的本质。更重要的是,它是开源的,大家都很容易从网上找到它。于是,Pluto就成为我们进行实践的不二之选了。OK,想好了就开始动手咯!
        首先,我们从Pluto下载页面
中把最新版本1.0.1-rc4的Pluto给download下来。由于Portlet Container并不是一个可以独立运行的container,它是离不开Web Server和Servlet Container的,所下载的Pluto中就包含了Tomcat5.5.9。由于Tomcat5.5.9所要求的JRE是5.0或以上,如果你的机子上装有5.0以上版本的JRE,那么你只需要将Pluto解压到某一个目录下(<PLUTO-HOME>),然后在console中进入<PLUTO-HOME>/bin目录,运行startup即可。这样Tomcat连同Pluto都启动了,接着我们访问http://localhost:8080/pluto/portal,就会出现如下界面,准备工作就大功告成了。

Pluto.bmp

如果你的机子上只有JRE1.4的话,要让Tomcat跑起来我们就还得从Tomcat5.5.9的下载页面下载一个名为compat的zip file。然后把这个zipfile解开,里面包含着bin和common两个目录,bin目录下包含着jmx.jar文件,common目录下还包含着一个endorsed目录,该目录下包含着xml-apis.jar和xercesImpl.jar文件。我们只需要把jmx.jar复制到<PLUTO-HOME>/bin目录下,改掉<PLUTO-HOME>/common/endored目录下的两个jar文件的后缀,并将xml-apis.jar和xercesImpl.jar复制到该目录下即可。
        准备工作完成以后,我们就开始咱们的第一个Portlet了——HelloWorld Portlet,具体的步骤请参考OnJava上的文章:What Is a Portlet, 在这里我就不罗嗦了。与这篇文章所介绍的不同,由于我们使用的是1.0.1-rc4,发布Portlet就不再像文章所说的那样复杂了,你也不需要有 Maven的基础,只需要通过Pluto的Admin页面将war文件上传并根据页面提示进行少许配置,就轻而易举地完成Portlet的发布了。发布完 成以后,需要重新启动Pluto,这是一个非常不方便的地方,但据说在一个版本的Pluto将支持Hot Deploy,发布Portlet就不需要重新启动了。
        最后,我们通过浏览器访问http://localhost:8080/pluto/portal,你会发现在左边的Portal Page列表中多了一项——HelloWorld。点击该项,我们就可以看到这个最简单的Portlet了。

HelloWorldPortlet.bmp

[教程]ASP.net MVC Framework框架安装正确步骤

mikel阅读(705)

1.运行安装文件,执行安装
安装后,因为默认安装的是针对英文版的VS2008,安装文件夹为1033,但是中文VS2008的安装文件夹为2052,因此需要将1033文件夹改名或将内容拷贝到2052文件夹下
具体步骤如下:
1.将C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\Web\MVC\1033
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplatesCache\Web\MVC\1033
文件夹改名为2052
2.将C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ProjectTemplates\Test\1033
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ProjectTemplates\Web\1033
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ProjectTemplatesCache\Test\1033
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ProjectTemplatesCache\Web\1033
拷贝到对应路径下的2052文件夹中
记得一定要将Test文件夹下的1033内容拷贝到2052中,否则创建项目时不会创建测试项目。

[软件]VS2008Team版序列号

mikel阅读(643)

 VS2008破解方法非常简单,在开始>设置>控制面版>添加或删除程序>卸载vs.net2008>出现卸载界面>点击Next>输入上面CD-key ->出现成功画面即可完美将试用版升级成为正式版。
VS2008正式版序列号CDKEY:PYHYP-WXB3B-B2CCM-V9DX9-VDY8T

[教程]ASP.NET AJAX (Atlas) 拖放(Drag &amp; Drop)功能6种实现

mikel阅读(773)

在Ajax程序中实现传统桌面程序中异常简单的拖放功能却并不是件容易的事情。然而Web上的拖放功能又如此的让人痴迷,所以几乎每个成熟的 Ajax类库都提供了自己的一套实现拖放的封装,ASP.NET AJAX (Atlas) 自然也不例外。本文将总结并简要分析ASP.NET AJAX (Atlas) 中拖放功能的6种不同的实现方法,希望能够帮助朋友们选出最适合实际需求的方法。

其中第1到第4种方案,在我 的《ASP.NET Ajax程序设计——第I卷:服务器端ASP.NET 2.0 AJAX Extensions与ASP.NET AJAX Control Toolkit》一书中有详细介绍(4月出版),本文中的代码和图示也节选自该书。第5第6种方案,在我的《ASP.NET 2.0 Ajax程序设计——第II卷:客户端Microsoft AJAX Library》一书中将有详细介绍(暂定7月出版)。

不过纵观这些解决方案,我很遗憾的发现,要么是使用简单,可定制能力差,要么就是可定制能力强,但使用起来要写很多代码。希望ASP.NET AJAX (Atlas) 团队能够再接再厉,努力把这个重要功能做得更好。或者我有哪种方法漏掉了,也请朋友们帮忙补充一下。

 

[1] 使用服务器端DragOverlayExtender或客户端DragOverlayBehavior

服务器端的DragOverlayExtender就是靠着客户端DragOverlayBehavior而实现的,前者是后者在服务器端的组件化封装,所以我们将二者放在一起讨论。

这 个“拖放”功能很简单,其实这只是个“拖拽”,而没有“投放”。也就是说,你可以随意将某个Panel在页面中拖来拖去,不过却没有什么固定的地方可以 “投放”,就像在Windows桌面上拖放某个窗口的位置一样——其实用处不大,也没有提供什么可定制能力。但它使用起来非常简单,也支持将Panel的 位置保存在Profile中。

下面是一段DragOverlayExtender的示例代码,至于DragOverlayBehavior的用法,请查看DragOverlayExtender页面生成的HTML代码:

<asp:Login ID="floatLogin" BackColor="white"
  BorderStyle="Solid" BorderColor="black" 
  runat="server">
</asp:Login>
<asp:DragOverlayExtender ID="DragOverlayExtender2" 
  Enabled="true" TargetControlID="floatLogin" 
  runat="server" />

效果如下:

 

[2] 使用服务器端DragPanel

DragPanel是ASP.NET AJAX Control Toolkit中的一个扩展器控件。其功能基本与DragOverlayExtender和DragOverlayBehavior一样,且同样可以保存 Panel的位置信息至Profile中。不同之处在于,DragOverlayExtender和DragOverlayBehavior的拖拽实现方 式中,鼠标放在整个Panel的任何部分都可以开始拖拽,而DragPanel在进行拖拽时,只有鼠标放在指定的DragHandle(类似于 Windows窗口的标题栏部分)中才能开始拖拽。

对于DragHandle的扩展性和实用性,同样不敢恭维。

下面是一段示例代码:

<asp:Panel ID="floatPanel" CssClass="floatPanel" runat="server">
    <asp:Panel ID="floatPanelHandle" CssClass="handle" runat="server">
        窗口的拖动
    </asp:Panel>
    <div class="content">
        在Windows中,对窗口的拖动似乎成了我们习以为常的事情。
        ………………
        ………………
        Window窗口的表现行为一样。
    </div>
</asp:Panel>
<ajaxToolkit:DragPanelExtender ID="dpe" TargetControlID="floatPanel" 
    DragHandleID="floatPanelHandle" runat="server">
</ajaxToolkit:DragPanelExtender>

效果如下:

 

[3] 使用服务器端ReorderList

ASP.NET AJAX Control Toolkit中的ReorderList控件将在页面中呈现出一个由数据绑定自动生成的条目列表。用户可以通过鼠标拖动某一项来直接改变该列表中条目彼 此之间的相对位置关系,且在拖动的过程中,ReorderList控件提供了丰富的、可定制的视觉效果。当用户在某个位置放开鼠标之后, ReorderList控件也将同样会自动通知与其绑定的数据源控件,以Ajax的异步或整页回送的同步方式更新服务器端数据。

ReorderList控件的使用比较简单(服务器端控件),功能也相当的丰富,扩展能力也不错。不过仍称不上最灵活,比如我们想把条目在多个ReorderList之间拖放,那么就没办法实现了——因此,不要指望它能实现WebPart那样的功能。

下面是一段示例代码:

<ajaxToolkit:ReorderList ID="musicList" CssClass="musicList" 
  DragHandleAlignment="Left" PostBackOnReorder="false" 
  DataSourceID="musicDataSource" DataKeyField="Id" 
  SortOrderField="Order" runat="server">
    <ItemTemplate>
        <ajaxToolkit:Rating ID="rating" runat="server" 
           CssClass="rating" StarCssClass="ratingStar" 
           FilledStarCssClass="filledRatingStar" EmptyStarCssClass="emptyRatingStar" 
           CurrentRating='<%# Bind("Rating") %>' MaxRating="5" 
           ReadOnly="true">
        </ajaxToolkit:Rating>
    </ItemTemplate>
    <ReorderTemplate>
        <div class="dragDue">
            Drop Here!
        </div>
    </ReorderTemplate>
    <DragHandleTemplate>
        <asp:Label ID="lbTitle" CssClass="dragHandle" 
           ToolTip="Drag Me!" runat="server" Text='<%# Bind("Name") %>'>
        </asp:Label>
    </DragHandleTemplate>
</ajaxToolkit:ReorderList>

效果挺酷的:

 

[4] 使用UpdatePanel与ASP.NET AJAX中的新版本WebPart控件

ASP.NET 2.0中的WebPart相关的控件虽然非常丰富,易于使用且功能强大,我们在程序中也很需要它所提供的拖拽功能,但它却存在着两个致命的缺陷:

  1. 拖放功能只支持IE浏览器。
  2. 每次用户通过拖放改变配件的位置之后,页面总会自动进行回送以保存当前的设定。

其中第一个问题可以通过ASP.NET AJAX中的新版本WebPart控件搞定,第二个问题可以通过添加UpdatePanel搞定,本来在CTP版本中的ASP.NET AJAX里面,这些功能均已经完美实现了。谁知道在最新的Futures CTP中,却又不好用了。

既然现在已经不能用了,也就没必要分析其优点缺点了。不过还是给出一张截图吧,缅怀一下曾经的辉煌(注意,这可是在Firefox中啊!)……

 

[5] 使用客户端DragDropList

DragDropList定义于ASP.NET AJAX Futures CTP中,功能非常强大,且全部在客户端实现,给服务器端减轻了不少的压力。使用DragDropList实现第4种解决方案中的WebPart类似的效 果完全没有问题,不过唯一让人感到遗憾的就是,其扩展功能不是很好,用户虽然可以随便将某个Panel从一个区域拖到另一个区域,但拖拽的结果却很难持久 化下来……且使用起来也不是那么的容易,效率上更是不敢恭维……总之,这是个很让人矛盾的东西,有些鸡肋的感觉。

我曾经在《使用ASP.NET Atlas实现拖放(Drag & Drop)效果(下)》这篇文章中给出过DragDropList的示例程序,虽然是基于CTP版本的ASP.NET AJAX ,不过实际上并不需要多少修改就能运行于最新版本之上。感兴趣的朋友可以看看,也可以到官方论坛上搜索一下相关的主题。

 

[6] 在客户端自行实现IDragSource和IDropTarget接口

来到了这个解决方案,可以说你对ASP.NET AJAX中的拖拽实现有了一个较深入的了解。上面的DragDropList其实就是实现了IDragSource和IDropTarget接口,其中前 者用来定义可以被拖拽的项目,后者用来定义可以被投放的区域。详细理论上的说明,可以参考我的文章《使用ASP.NET Atlas实现拖放(Drag & Drop)效果(上)》,虽然目前已经有所变化,不过仍可以参考。

这种实现方案应该说是最为强大的了,想要什么功能,肯定都能实现。不过实际开发中的难度也不小——甚至可以说是相当复杂,Jeff Prosise的这篇文章《Implementing Drag-Drop in ASP.NET AJAX》给出了一个非常简单的示例程序,感兴趣想要学习的朋友不妨看看……

[教程]C#的反射机制实例

mikel阅读(828)

看了http://www.cnblogs.com/zhenl/archive/2004/10/27/57456.aspx,觉得不爽,改了一下顺便装成C#了.
让我们看这样一个例子,我们需要创建一种交通工具,可以是汽车、火车或者轮船,结构如下:

我们可以采用简单工厂,通过参数告诉创建工厂我们所需要的对象类型。如果我们增加子类,比如卡车、轿车等等,我们必须增加参数和相应的代码,如果子类层次很多,就会使程序变得难以维护。如果用简单工厂实现上面的结构显然很烦琐。
当然,我们可以采用工厂方法来实现,我们可以定义一个产生交通工具的接口,然后在子类中实现创建具体的子类:
'采用接口定义了抽象的工厂方法
Public Interface CreateVehicle
    Function CreateAVehicle() As Vehicle '创建一个交通工具
End Interface
 
'具体的创建由子类决定
Public Class CreateCar
    Implements CreateVehicle
    Public Function CreateAVehicle() As Vehicle Implements CreateVehicle.CreateAVehicle
        Return New Car
    End Function
End Class
 
这就是工厂方法。但如果我们希望增加一个新的交通工具,不仅需要实现交通工具接口,还需要实现产生交通工具的工厂方法。下面是船的具体工厂方法:
 
Public Class CreateBoat
    Implements CreateVehicle
    Public Function CreateAVehicle() As Vehicle Implements CreateVehicle.CreateAVehicle
        Return New Boat
    End Function
End Class
 
显然,如果我们需要产生几十种交通工具的话,就需要有几十个具体的工厂类,而这些工厂类的区别仅仅是返回相对应的类的实例。这为维护带来了麻烦,如果需要在接口中增加一个带参数的创建方法,则所有的子类都需要修改。
采用抽象工厂如何呢?在这个场合下,抽象工厂与工厂方法没有什么区别,因为这里并不涉及产品线,抽象工厂并不能解决这里的问题。当然,如果每种交通工具都要有对应的车站的话,就要使用抽象工厂,但那样将更复杂。
有没有可能将需要创建的类的类型传递到工厂方法中,由工厂方法根据类型返回相应的实例呢?
解决上面的问题的关键是需要动态决定需要创建的类,这不是设计模式能解决的问题,属于软件平台的功能范畴。好在.net可以提供相应的功能,这就是反射技术。
看一下采用反射技术实现的实例:

   
   
using System;
using System.Reflection;
namespace ConsoleApplication1
{
    
//交通工具的统一接口
    public interface IVehicle
    
{
        
string Color{get;}
    }

    
//一辆汽车
    public class Car : IVehicle
    
{
        
public Car(){}
        
public string Color
        
{
            
get
            
{
                
return "Blue";
            }

        }

    }

    
//一艘船
    public class Boat : IVehicle
    
{
        
public Boat(){}
        
public string Color
        
{
            
get
            
{
                
return "RED";
            }

        }

    }


    
//交通工具制造工厂
    public class VehicleFactory
    
{
        
public static IVehicle CreateVehicle(Type vehicleType)
        
{
            
//自己写构造参数哦!
            ConstructorInfo construct 
                
= vehicleType.GetConstructor(Type.EmptyTypes);
            IVehicle v 
= (IVehicle)construct.Invoke(null);
            
return v;
        }

    }

}


   
在使用时,只要在创建时带入需要创建的类的类型就可以了。
using System;

namespace ConsoleApplication1
{
    
/// <summary>
    
/// TestFactory 的摘要说明。
    
/// </summary>

    public class TestFactory
    
{
        [STAThread]
        
static void Main(string[] args)
        
{
            IVehicle b 
= VehicleFactory.CreateVehicle(typeof(Car));
            Console.WriteLine(b.Color);
            Console.Read();
        }

    }

}
在使用时,只要在创建时带入需要创建的类的类型就可以了。

[教程]如何用C#实现依赖注入?

mikel阅读(778)

1. 问题的提出

开发中,尤其是大型项目的开发中,为了降低模块间、类间的耦合关系,比较提 倡基于接口开发,但在实现中也必须面临最终是“谁”提供实体类的问题。Martin Fowler在《Inversion of Control Containers and the Dependency Injection pattern》中也提到了标准的三种实现方式——Constructor Injection、Setter Injection和Interface Injection,很全面的阐释了这个问题。
对于C#而言,由于语法元素上本身要比Java丰富,如何实施注入还有些技巧和特色之处。这方面微软的ObjectBuilder是个不错的教科书,对三种标准方式的实现也都很到位,但就是有些庞大了。
本文中,笔者借鉴Martin Fowler的撰文,也通过一些精简的代码片断向读者介绍C#实现依赖注入的基本技巧。
我有个习惯,每天晚上要看天气预报,就以这个开始好了,先定义待注入对象的抽象行为描述,然后增加一个假的实体类,相关代码和单元测试如下:

C#
using System;
namespace VisionLogic.Training.DependencyInjection.Scenario
{
/// <summary>
/// 抽象注入对象接口
/// </summary>
public interface IWeatherReader
{
string Current { get;}
}
}
C#
using System;
namespace VisionLogic.Training.DependencyInjection.Scenario.Raw
{
/// <summary>
/// 伪造的一个实现类
/// </summary>
class FakeWeatherReader : IWeatherReader
{
public string Current { get { return string.Empty; } }
}
/// <summary>
/// 客户程序
/// </summary>
public class Client
{
protected IWeatherReader reader = new FakeWeatherReader();
public virtual string Weather
{
get
{
string current = reader.Current;
switch (current)
{
case "s": return "sunny";
case "r": return "rainy";
case "c": return "cloudy";
default:
return "unknown";
}
}
}
}
}
Unit Test
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario;
using VisionLogic.Training.DependencyInjection.Scenario.Raw;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest.Raw
{
[TestClass]
public class WeatherReaderTest
{
[TestMethod]
public void Test()
{
Client client = new Client();
Assert.AreEqual<string>("unknown", client.Weather);
}
}
}

    问题就出现了,虽然美好的愿望是Client仅仅依赖抽象的IWeatherReader,但之前总要和一个实体类“轧”一道,那么实际的效果就是实体类 作了修改、重新编译了,Client也要处理,没有真正达到隔离的目的。依赖注入通过引入第三方责任者的方法,相对好的梳理了这个关系,这位重要的角色就 是一个Assembler类,他和实体类型打交道,对Client而言他总是可以根据约定,加工出需要的IWeatherReader。

2.进一步的分析
    看上去,Client被解放了,但又套住了Assembler,为了尽量让他与实体类间松散些需要做什么呢?
 首先要完成自己的职责:可以找到合适的实现类实例,不管是重新构造一个还是找个现成的。
 既要根据需要加工接口IWeatherReader,又要让自己尽量不与大量的实体类纠缠在一起,最好的办法就是从.Net Framework中再找到一个“第三方”,这里选中了System.Activator。
 还有就是当客户程序调用Assembler的时候,它需要知道需要通过哪个实现类的实例返回,该项工作一方面可以通过一个字典完成,也可以通过配置解决,两者应用都很普遍,怎么选择呢——抽象,提取一个接口,然后都实现。
由于本文主要介绍依赖注入的实现,为了简单起见,采用一个伪造的内存字典方式,而非基于System.Configuration的配置系统实现一个Assembler的协同类。

    C# 新增一个用于管理抽象类型——实体类型映射关系的类型ITypeMap
using System;
using System.Collections.Generic;
namespace VisionLogic.Training.DependencyInjection.Scenario
{
/// <summary>
/// 考虑到某些类型没有无参的构造函数,增加了描述构造信息的专门结构
/// </summary>
public class TypeConstructor
{
private Type type;
private object[] constructorParameters;
public TypeConstructor(Type type, params object[] constructorParameters)
{
this.type = type;
this.constructorParameters = constructorParameters;
}
public TypeConstructor(Type type) : this(type, null) { }
public Type Type { get { return type; } }
public object[] ConstructorParameters { get { return constructorParameters; } }
}
/// <summary>
/// 管理抽象类型与实体类型的字典类型
/// </summary>
public interface ITypeMap
{
TypeConstructor this[Type target]{get;}
}
}
C# 实现一个Assembler类型,为了示例方便,同时实现了一个ITypeMap和IWeatherReader
using System;
using System.Collections.Generic;
namespace VisionLogic.Training.DependencyInjection.Scenario
{
/// <summary>
/// 测试用的实体类
/// </summary>
public class WeatherReaderImpl : IWeatherReader
{
private string weather;
public WeatherReaderImpl(string weather)
{
this.weather = weather;
}
public string Current
{
get { return weather; }
}
}
/// <summary>
/// 管理抽象类型与实际实体类型映射关系,实际工程中应该从配置系统、参数系统获得。
/// 这里为了示例方便,采用了一个纯内存字典的方式。
/// </summary>
public class MemoryTypeMap : ITypeMap
{
private Dictionary<Type, TypeConstructor> dictionary =
new Dictionary<Type, TypeConstructor>();
public static readonly ITypeMap Instance;
/// <summary>
/// Singleton
/// </summary>
private MemoryTypeMap(){}
static MemoryTypeMap()
{
MemoryTypeMap singleton = new MemoryTypeMap();
// 注册抽象类型需要使用的实体类型
// 该类型实体具有构造参数,实际的配置信息可以从外层机制获得。
singleton.dictionary.Add(typeof(IWeatherReader), new TypeConstructor(
typeof(WeatherReaderImpl), "s"));
Instance = singleton;
}
/// <summary>
/// 根据注册的目标抽象类型,返回一个实体类型及其构造参数数组
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public TypeConstructor this[Type type]
{
get
{
TypeConstructor result;
if (!dictionary.TryGetValue(type, out result))
return null;
else
return result;
}
}
}
public class Assembler<T>
where T : class
{
/// <summary>
/// 其实TypeMap工程上本身就是个需要注入的类型,可以通过访问配置系统获得,
/// 这里为了示例的方便,手工配置了一些类型映射信息。
/// </summary>
private static ITypeMap map = MemoryTypeMap.Instance;
public T Create()
{
TypeConstructor constructor = map[typeof(T)];
if (constructor != null)
{
if (constructor.ConstructorParameters == null)
return (T)Activator.CreateInstance(constructor.Type);
else
return (T)Activator.CreateInstance(
constructor.Type, constructor.ConstructorParameters);
}
else
return null;
}
}
}
Unit Test
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest
{
[TestClass()]
public class AssemblerTest
{
[TestMethod]
public void Test()
{
IWeatherReader reader = new Assembler<IWeatherReader>().Create();
Assert.IsNotNull(reader);
Assert.AreEqual<System.Type>(typeof(WeatherReaderImpl), reader.GetType());
}
}
}

3.经典方式下的注入实现

    在完成了Assembler这个基础环境后,就是怎么注入的问题了,下面是对三种方式的经典方法实现:
    3.1 Constructor Injection方式

Unit Test – Constructor
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest
{
[TestClass]
public class ConstructorInjectionTest
{
class Client
{
private IWeatherReader reader;
public Client(IWeatherReader reader)
{
this.reader = reader;
}
}
[TestMethod]
public void Test()
{
IWeatherReader reader = new Assembler<IWeatherReader>().Create();
Client client = new Client(reader);
Assert.IsNotNull(client);
}
}
}

    3.2 Setter Injection方式

Unit Test – Setter
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest
{
[TestClass]
public class SetterInjectionTest
{
class Client
{
private IWeatherReader reader;
public IWeatherReader Reader
{
get { return reader; }
set { reader = value; }
}
}
[TestMethod]
public void Test()
{
IWeatherReader reader = new Assembler<IWeatherReader>().Create();
Client client = new Client();
client.Reader = reader;
Assert.IsNotNull(client.Reader);
}
}
}
    3.3 Interface Injection方式
Unit Test – Interface
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest
{
[TestClass]
public class InterfaceInjectionTest
{
interface IClientWithWeatherReader
{
IWeatherReader Reader { get; set;}
}
class Client : IClientWithWeatherReader
{
private IWeatherReader reader;
#region IClientWithWeatherReader Members
public IWeatherReader Reader
{
get { return reader; }
set { reader = value; }
}
#endregion
}
[TestMethod]
public void Test()
{
IWeatherReader reader = new Assembler<IWeatherReader>().Create();
Client client = new Client();
IClientWithWeatherReader clientWithReader = client;
clientWithReader.Reader = reader;
Assert.IsNotNull(clientWithReader.Reader);
}
}
}

4. 用属性(Attribute)注入 
    C#还可以通过Attribute注入,Enterprise Library中大量使用这种方式将各种第三方机制加入到类系统中。例如:
 •运行监控需要的Performance Counter。
 •用于构造过程的指标信息。
 •用于日志、密码处理。
 •等等 
    注:Java语言虽然发展比较慢,但在Java 5种也提供了类似的Annotation的机制,换了个名字省去被评估为“抄袭”的嫌疑。) 
    为了演示方便,下面设计一个应用情景: 
    
    Scenario 
    1、 应用需要一个集中的机制了解系统中实际创建过多少个特定类型对象的实例,用于评估系统的Capacity要求。 
    2、 为了防止系统资源被用尽,需要控制每类对象实例数量。 
    怎么实现呢?如下:
 •增加一个内存的注册器,登记每个类已经创建过的实例实例数量。
 •然后给每个类贴个标签——Attribute,让Assembler在生成的对象的时候根据标签的内容把把登记到注册器。 
    4.1定义抽象业务实体
C#
using System;
namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer
{
/// <summary>
/// 抽象的处理对象
/// </summary>
public interface IObjectWithGuid
{
string Guid { get; set;}
}
}
定义需要注入的限制接口,并用一个Attribute管理它
C#
using System;
namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer
{
/// <summary>
/// 需要注入的用以限制最大数量的接口
/// </summary>
public interface ICapacityConstraint
{
int Max { get;}
}
public class CapacityConstraint : ICapacityConstraint
{
private int max;
public CapacityConstraint(){this.max = 0;} // 默认情况下不限制
public CapacityConstraint(int max) { this.max = max; }
public int Max { get { return max; } }
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ConstraintAttribute : Attribute
{
private ICapacityConstraint capacity;
public ConstraintAttribute(int max) { this.capacity = new CapacityConstraint(max); }
public ConstraintAttribute() { this.capacity = null; }
public ICapacityConstraint Capacity { get { return capacity; } }
}
}
Assembler上增加通过Attribute注入限制的响应
C#
using System;
using System.Collections.Generic;
namespace VisionLogic.Training.DependencyInjection.Scenario.Attributer
{
public class Assembler
{
/// <summary>
/// 登记相关类型对“最大容量”属性的使用情况
/// </summary>
private IDictionary<Type, ConstraintAttribute> attributeRegistry =
new Dictionary<Type, ConstraintAttribute>();
/// <summary>
/// 登记每个类型(如须受到“最大容量”属性限制的话),实际已经创建的对象数量
/// </summary>
private IDictionary<Type, int> usageRegistry = new Dictionary<Type, int>();
public T Create<T>()
where T : IObjectWithGuid, new()
{
ICapacityConstraint constraint = GetAttributeDefinedMax(typeof(T));
if ((constraint == null) || (constraint.Max <= 0)) // max <= 0 代表是不需要限制数量的。
return InternalCreate<T>();
else
{
if (usageRegistry[typeof(T)] < constraint.Max) // 检查是否超出容量限制
{
usageRegistry[typeof(T)]++; // 更新使用情况注册信息
return InternalCreate<T>();
}
else
return default(T);
}
}
// helper method
// 直接生成特定实例,并setter 方式注入其guid。
private T InternalCreate<T>()
where T : IObjectWithGuid, new()
{
T result = new T();
result.Guid = Guid.NewGuid().ToString();
return result;
}
/// helper method.
// 获取特定类型所定义的最大数量, 同时视情况维护attributeRegistry 和usageRegistry 的注册信息。
private ICapacityConstraint GetAttributeDefinedMax(Type type)
{
ConstraintAttribute attribute = null;
if (!attributeRegistry.TryGetValue(type, out attribute)) //新的待创建的类型
{
// 填充相关类型的“最大容量”属性注册信息
object[] attributes = type.GetCustomAttributes(typeof(ConstraintAttribute), false);
if ((attributes == null) || (attributes.Length <= 0))
attributeRegistry.Add(type, null);
else
{
attribute = (ConstraintAttribute)attributes[0];
attributeRegistry.Add(type, attribute);
usageRegistry.Add(type, 0); // 同时补充该类型的使用情况注册信息
}
}
if (attribute == null)
return null;
else
return attribute.Capacity;
}
}
}
    4.2对方案的测试

C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
using VisionLogic.Training.DependencyInjection.Scenario.Attributer;
namespace VisionLogic.Training.DependencyInjection.Scenario.UnitTest.Attributer
{
[TestClass()]
public class AssemblerTest
{
public abstract class ObjectWithGuidBase : IObjectWithGuid
{
protected string guid;
public virtual string Guid
{
get { return guid; }
set { guid = value; }
}
}
[Constraint(2)] // 通过属性注入限制
public class ObjectWithGuidImplA : ObjectWithGuidBase { }
[Constraint(0)] // 通过属性注入限制
public class ObjectWithGuidImplB : ObjectWithGuidBase { }
[Constraint(-5)] // 通过属性注入限制
public class ObjectWithGuidImplC : ObjectWithGuidBase { }
public class ObjectWithGuidImplD : ObjectWithGuidBase { }
[TestMethod]
public void Test()
{
Assembler assembler = new Assembler();
for (int i = 0; i < 2; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplA>());
Assert.IsNull(assembler.Create<ObjectWithGuidImplA>()); // 最多两个
for (int i = 0; i < 100; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplB>()); // 不限制
for (int i = 0; i < 100; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplC>()); // 不限制
for (int i = 0; i < 100; i++)
Assert.IsNotNull(assembler.Create<ObjectWithGuidImplD>()); // 不限制
}
}
}
    5. 进一步讨论

    上面的例子虽然仅仅通过Attribute注入了一个容量限制接口,但完全可以为他增加一个容器,可以把一组限制接口借助ConstraintAttribute这个通道注入进去,并在Assembler中生效。
    6.其他问题

    实际项目中,为了满足多核系统的需要,Assembler往往和目标对象分别运行在主进程和具体某个线程之中,如何线程安全的注入是必须面临并谨慎设计的问题。

[教程]C#的反射机制

mikel阅读(958)

何为反射?
    反射指的是一个编程技巧,在当前应用领域的运行时间内,开发者可以利用它授权一个程序集检查并修改自身或其他程序集。那些检查能够让开发者执行比用其他方 法实现的更为强大的后期捆绑功能。反射是一个较新的微软开发方法;但是,Java和其他现代语言包含了与之十分相似的功能。作为.NET Framework的一项特色,反射能够被任何与.NET兼容的语言应用,如C#、VB.NET或J#。与本文的可下载版本一同的C#样本项目说明了本文 介绍的一些技巧。
    反射的运作模式?
    .NET Framework中生成的每个程序集都包含了描述程序集以及程序集中的类或结构的元数据。应用这个元数据,反射能够列举一个程序集所包含的一切类、结构和数据。一旦我们了解某一程序集的功能,就可能修改并利用这些功能。
    为说明反射如何应用元数据,仔细研究下面从样本C#项目中摘录的代码。

private string ListProperties(object objectToInspect) { string returnString = ""; //To use reflection on an object, you // first need to get an instance // of that object's type. Type objectType = objectToInspect.GetType(); //After you have the object's type, you can get // information on that type. In this case, we're // asking the type to tell us all the // properties that it contains. PropertyInfo[] properties = objectType.GetProperties(); //You can then use the PropertyInfo array // to loop through each property of the type. foreach(PropertyInfo property in properties) { //The interest part of this code // is the GetValue method. This method // returns the value of the property. returnString += property.Name + ": " + property.GetValue(objectToInspect, null) + "\r\n"; } return returnString; }

    在这段代码中,我们列举了一个类包含的属性。为完成这一点,反射应用程序集中的元数据返回一个包含类中的属性的PropertyInfo数组。然后我们循环遍历PropertyInfo数组,并单独访问每个属性。
    在应用反射时,阅读元数据的过程对开发者是开放的。但是,反射应用元数据这一事实是重点。
    应用反射

    反射可用来执行一系列的功能。大多数的序列化引擎应用反射从需要序列化的对象中提取数据。反射还可以用内置模块来建立可自定义的应用结构。许多对象关系建模(ORM)还利用反射来执行动态映射功能。
    虽然没有进行广泛宣传,反射的最有趣应用之一在于它能大大减少完成单调开发任务所需的时间。由于反射是完全动态的,它允许你在不必重新生成代码的情况下改变基本的结构,因而反射是一个非常优秀的代码生成替代工具。
    例如,许多代码生成工具允许你根据基本的数据库建立数据对象。反射可用来从DataTables——它由数据库中返回——这样的数据结构中动态提取商业对 象(类)。应用这种方法,就不必应用代码生成器来生成代码。你只要简单地循环遍历DataTables中的竖栏,并将栏名称与商业对象中的属性名称动态匹 配。
    还可用反射将一个对象的属性与一个存储程序参数动态捆绑起来。这个捆绑过程允许你建立普通的数据访问方法,提供代码生成工具为你带来的许多完全动态的优势。因此,反射能够显著减少代码长度,并缓解应用维护压力。
    简单介绍
    在本文中,我们只是触及问题的表层,说明反射能够帮助开发者编写更好的代码。在以后的文章中,我将以这篇介绍性的文章为基础,更加详细的说明反射的其他应 用与功能。我要讨论的主题包括访问方法和在运行时的类属性、从数据结构中提取商业对象、以及建立一个可由多个项目应用的普通数据访问层。

[原创]面向生命(Life oriented 简称 LO)编程

mikel阅读(688)

序言:
软件是个生命体!
万事万物皆变化!
为什么我会提出”面向生命(Life oriented)”编程的思想,因为我们一直以来以建筑学的角度来审视软件工程,将软件定义为一种无生命的产品,而事实是外界的需求的无时无刻都在变化要求我们做出来的软件也随之变化,你难道想让一所盖好的房子会每时每刻根据外界的环境变化吗?而有生命的植物和动物则可以,会依据环境的变化而变化,一株草会根据压在上方的石头而改变自身的生长形态而曲折生长,软件应该也会为适应变化而调整自身的结构和功能来避免被变化的环境所淘汰!没错这是个适者生存的世界,软件也一样。
还有一个原因就是软件和人一样是有生、老、病、死的,诞生的时候具有面向对象思想中人类的基本属性和行为,但是还很弱,就像婴儿一样,但是面向对象的思想将世界万物都抽象成类,类的实例创建了对象,但是根本没有发现一类的对象之间是存在差异的,难道你看到过世界上长的完全一模一样的人吗?没有,因为每个人都是鲜活的生命体,尽管具有人类的共同属性和行为,但是每个人都有自己的性格、学习能力、、、、、、、,同时每个人的成长历程是根据环境的不同而不同,也就是适应变化,刚生下来的婴儿是最原始的对象,然后通过人类具有的基本行为嗅觉、听觉、视觉、味觉、触觉、学习能力来获得信息不断完善对世界的认识,从而形成不同的性格,也逐渐成长起来从需要外界服务,到能够对外界提供服务,而每个人接受的知识和自己的能力决定了最终他能够为外界提供哪种服务,随着时间周期和自身的消耗慢慢老化,直至死亡,软件也是如此,开始我们只是依据目前的静止的需求将其创建出来,可我们错就错在用一种静止的眼光去看待软件,其实它是在我们的不断提供信息的过程中,不断成长,同时从我们提供的信息中它在学习强化了自身的功能,并能够提供给我们更多功能,也就是目前各大网站提供的对外接口服务,其实yahoo或google开始这些网站系统是没有能力提供这些服务的,为什么是因为它们没有足够支持这些服务的信息,就好比它们开始只是婴儿还需要我们提供信息给它,让它成长一样,当它在我们不断的信息营养下成长成为一个巨人的时候,它们反过来能够为我们做更多以往我们都没有想过能做的事,这就是一个软件生命体,而不是我们传统意义上的建筑,建筑的功能是有限的而生命体的功能是无限的,因此你硬要将一个不变的软件去适应不断变化的环境那是种错误!因此,我们需要一种全新的思想去指导我们去设计软件那就是“面向生命”!
第一章:什么是面向生命编程
面向生命编程是以面向对象为基础,将对象赋予生命特质的一种编程思想。
面向生命编程将世界中具有相同属性和行为的事物化为为”类“,”类“的具体实例称为”对象“,每个对象都具有“繁殖”能力,繁殖的定义是同类的一个或多个对象通过“结合”产生”亲子对象“,”亲子对象“具有父类的”遗传“属性和行为,同时具有特有的”个性“的属性和行为,“生命体”是独立的个体,它具有“学习”、“分析”、“判断”、“成长”、“创造”能力,它通过对外界提供给它的信息内容利用“判断”和“分析”后,经过“学习”然后“创造”出”自制“的功能,以”服务“的方式提供给外界进行使用。”生命体”具有“模仿”能力,它通过外界提供的对某种功能的“描述”然后对描述进行“分析”、“判断”然后结合自身的“信息”资源进行“创造”出具有同等功能的能力从而达到“模仿”。
生命与应用系统的对应关系图:

生命体:就是最终的应用软件系统
骨骼:等同于应用系统的目录结构,用于支撑整个系统运行的整体的存储结构。
神经系统:就是大脑,是整个软件系统的核心,用于支配和调度各个”服务单元“运行实现”能力“的中枢,本身具有”记忆“、”学习“、”分析“的能力。
器官:对应软件系统中的服务单元,只具有某种能力的独立单元,通过对外界提供服务来实现某种能力,同时也具有自己的学习、分析、能力,对采集到的外界信息进行汇总分析并进行学习,来完成新能力的实现过程。
血液:对应软件系统中流淌的信息元,也就是独立的信息实体对象,每个信息对象也是独立的生命体,就像社会中的人一样,它具有独立的能力,承载独立的个性,穿行与整个系统中,并在神经系统的控制下在各个器官间穿行,为器官提供信息,同时获得器官处理后的信息。是整个系统的动力源泉,没有血液的系统是个死生命体,有了血液才能让生命体不断的成长。
皮肤:对外的表现形势,可以是页面、xml、、、等等用户易于操作的形势,甚至是视频或是图片、声音、信号等媒体

[教程]说说大型高并发高负载网站的系统架构

mikel阅读(725)

http://blog.csdn.net/starxu85/archive/2008/02/24/2116593.aspx

一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站 对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用 的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站 所能比拟的。
大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。
上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。
1、HTML静态化
其 实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有 效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新 闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管 理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。
同 时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论 坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分 内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。
2、图片服务器分离
大家知道,对于Web服务器来说,不管 是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片 服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务 器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消 耗和执行效率。
3、数据库集群和库表散列
大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。
上 面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最 有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能 进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架 构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统 随时增加一台低成本的数据库进来补充系统性能。
4、缓存
缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
网 站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大 型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多 了,.net不是很熟悉,相信也肯定有。
5、镜像
镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网 络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实 时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等 工具。
6、负载均衡
负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。
负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。
硬件四层交换
第 四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚 IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的 载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决 定。
在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了。
软件四层交换
大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。
软 件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满 足多种应用需求,这对于分布式的系统来说必不可少。
一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。
对于大型网站来说,前面提到的每个方法可能都会被同时使用到,我这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会,有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大,希望大家一起讨论,达到抛砖引玉之效。