[转载]使用"增强现实"的3D绘图API来增强角色形象的活泼性

mikel阅读(1381)

[转载]使用”增强现实”的3D绘图API来增强角色形象的活泼性 | 开发人员中心.

使用”增强现实”的3D绘图API来增强角色形象的活泼性

我为一个卡通商务卡片项目做开发时,得知了一种利用“增强现实”技术基于2D方块模型来显示3D动画效果的新概念。导致的结果就是,制作出来的 产品允许你使用独一无二的打印到背景上的色彩代码,来创建一个商务卡片。当你将这张彩色的代码拿到一个摄像头前时,一个卡通形象就显示在界面中,并开始说 话! 我们称这种改进后的数码商务卡片为 BOW cARds*你可以通过观看这段youtube上的视频来了解这种利用增强现实技术 的商务卡片式*如何工作的。

这篇文章描述了我们的团队开发BOW cARds的过程。 我们从2008年开始使用AR进行工作,就在我们的机构 BOW刚刚成形之后。之后,我们的团队决定专注于研发卡通形象的商务卡片,这种卡片使用在带有摄像头的普通笔记本电脑上,因为我们了解,打印在2D纸张上 的图样是商务卡片一种最自然的表现形式。

我们决定使用时下越来越流行的FLARToolKit*。 在没有遇到任何问题的情况下,我们结束了第一轮测试。下一步,陷入一个困难过程,就是创建三个独立的3D模型,当摄像头捕捉到对应的标记图形时,将模型显 示在屏幕中。当我们完全证实了我们的概念后,我们便投入了全面的开发周期中,创建具备交互性的商务卡片,这样人们可以个性化他们的形象,并彼此交换他们。

必备条件

要完成这篇教程中的实例,你需要安装以下软件。

Flash CS4 Professional

外部代码库

绘制和渲染程序(可选)

开发一个可任意堆叠的标记图形库

我们在尝试让这个想法变成现实时遇到了两个障碍。 为了使卡通形象显示出来,能被摄像头捕捉到的标记图形必须在FLARToolKit初始化时,能够被标准多重标记识别器识别出来。 为了达成这个目的,我们使用了FLARMultiMarkerDetector*,这个东西就在FLARToolKit包中。 这种限制使得很难动态地添加标记图形,也很明显,如果这在随后变成可能,性能也会随着注册的图形的增加而受到严重的影响。

我们放弃了捕捉注册图形的策略,我们开发了一个策略,将常见的原理引入到图像认知中。 下面,我们将这些图像识别记录到系统中,为了分辨商务卡片,并显示出独一无二的,个性化的3D模型。

从扫描到的图形中提取数据的过程,与扫描机器码或者二维码的过程很相似。 稍后,我们考虑使用QR 代码*,但是,我们发现有太多的细节在低分辨率的摄像头上不能被很好的识别出来。

在进行了很多测试之后,我们决定放弃QR代码,它不适用于接收数据的摄像头可以自由移动的AR。商务卡片的代码,可以在任何普通的笔记本电脑上成功的渲染使用是非常重要的,所以我们开始开发我们自己的专有设计。当我们定义好了我们的代码体系后,便开始考虑下面的情况:

  • 单色 vs 多彩图形:决定打印出的标记使用的颜色。
  • 正如扫描到的一样,被身份识别系统正确的渲染成平面的图形。
  • 错误探知和错误校正,确保3D模型正确的现实。
  • 在低分辨率的摄像头下能保证正确的适应的方块线条数量。

在我们使用不同的形状的图形标记做研究之后,我们发现,在单色调代码情况下更容易保持高级别的精确度,然而着色的方块却使在每一个方格间存储更多的数据成为可能。

在另外一轮测试之后,我们决定在标记图形中使用四色代码。 这更进一步的证实了模型所需要的线条方块是越简单越好的。 我们也将基于这种色彩的方案应用于通用的商务卡片上——使用100%的CMYK四色墨水打印,来确保用户的家用级打印机也可以保持清晰的对比度。

当我们研发使用方向识别的指令时,我们发现在将图形旋转了90°之后,很难被摄像头捕捉和识别。如果图形的上边缘不能被正确识别,代码将不能被 成功的识别。 没有适合的辨识能力,就不可能确定方向来正确的显示3D图形。我们决定,让所有的图形都遵循同样一个简单的规则:在每个图形中,当你观察四个角的方格后会 发现,左上角的格子永远是蓝绿色,余下的四个角格将会是黑色和黄色。 这使得通过摄像头接收到的输入能够被按照正确的顺序所建立出来。

为了实现错误识别和错误校正,我们为错误认知(侦查)附加了一些数据构造,并提供原型数据(校正),确保在任何时候不正确的数据都能被捕捉到。 当我们对图形原型做认知测试时,还是发现有偶发的读取色块失败,但错误校正机制被证明是有效的。 我们讨论了N多方案来做错误校正,但是最后我们决定使用Hamming代码* ,因为它容易实施并适用于流体操作。

最后,我们决定了使用的线条方块的数量。 这个过程从核定我们计划使用的点和要存储的数据量开始。 在反复进行了不同的测试后,我们设法在一个5 x 5的网格中存储了33位的信息,从25个方格中提取45位数据(在排除方向认证之后),且合并了一个三级(15,11)的Hamming代码。在测试后, 我们确信,这种图形可以提供足够的数据量来描绘一个商务卡片账号。

接下来,我们的团队专注于开发详细的色彩代码。 我们开始尝试用色彩代码为每个团队成员建立可视化的模型。开始时,我们设计了一些适用于5×5方格阵的点图。不过,这些只是很即兴的方式绘制的,不像那些 完整的编码的ID这些图形导致了一些显示错误。为了解决这个问题,我们开发了一个特别的工具,可以在绘制过程中显示解码后和重编码之后的色彩代码。 解决所有错误的同时,同样也校正图形色彩,直到左右两幅图片完全一致。这个过程的结果是,我的同事Yoshikawa和Takahash在他们的卡片上各 自完成了一张微笑的脸,而Ishizaki则在他的卡片上绘制了一个 “石头”的角色。(见图1)。

Dot art used to specify the color codes that are scanned by  the webcam

图1. 点图用来指定颜色代码并被摄像头扫描。

为角色增加活力使得角色可以和其他人打招呼

从商务卡片成为一种人际交往和联系的工具开始,我们便有了一个想法,让卡片上的角色能够和别人交互说hello。当只有一个卡片被扫描时,角色通常只是安静的在卡片上不动。我们想让这更进一步,所以当你并排将两张卡片放在一起并扫描时,角色们就跑到一起开始打招呼了。

在其他项目中,你可以使用FLARToolKit 从图形中计算得出的定位方阵来确认其他角色的位置,但是这法子对我们不灵。测试表明,当两张卡片在真实空间中被并排放置在一起时,一些细小的错误识别经常 导致两个角色根本就不在网格的正确位置上。我们决定放弃使用3D模型,因为它不适合我们想要达到的目标。

我们决定用计算每个图样界面四个角落间的图形,来创建一个平面的“展示箱”。一旦这个箱子被确立,程序就可以确定角色是否应该存在在图样的界面中。(见图2)

Using the four corners of each marker pattern to create the  view box region

见图2. 使用四角定位来确立每个图样要创建的展示区域。

下面,我们又添加了功能使角色可以说话。 我们通过卡片使用者在Twitter上发送的消息完成了这个功能。 当第二次开发出来的角色出现在屏幕上时,使用者在Twitter上发送的信息就显示出来了。(见图3)

Avatars becoming aware of each other and sharing a  conversation via Twitter messages

图3. 角色们察觉到其他角色的存在,并通过Twitter上的信息互相交谈。

为角色定制选项,使得角色更加个性化

当我们定义了我们这个项目将要发展的方向,我们决定创建一个在线的系统,允许访问者设置他们的个性角色。 我们觉得,个性化使得角色更招人喜欢,并鼓励更多的用户参与到BOW cARd计划中来。

区别于常见的FLARToolKit程序,BOW cARd直接使用Adobe Flash Player 10中的3D绘图API取代Papervision3D。之所以这样决定是为了能进行更加流畅的操作,并在未来的版本中实现新的功能时保持现有的设置。 设计过程中,我们非常小心,维持Flash 绘图API的能力和限制间的均衡性。

我们清楚这对进行更复杂的z轴操作,多次调用三角图形绘制时的性能是相当有益的。 为了实现这一点,每个角色都使用单次调用 3D角色模型采用Metasequoia*创建,之后我们采用Perl脚本将其转化成一个ActionScript 3类文件。

身体上的所有部件(包括躯体,腿,和儿子)都被存储在一个单顶点的矢量图形中。 在渲染过程中,只有必要的顶点坐标的部分被合并,直排序,并通过三角绘图函数渲染。 这种改进使得在更大范围内个性化表现,显示个性化角色模型成为可能。(见图4)

Using the drawTriangles method to add custom body parts to the  avatar

图4 . 使用三角绘图函数为角色添加个性化的身体。

直接在角色上绘制图像

每天,使用BOW cARd技术创建的个性化角色都在增加,但是个性化的需求被简单拼接的身体部分所限制。要想在角色上做更多的个性化设置,我们决定使用添加图形绘制功能。 通常这种功能都在一种开发视图画布下实现,并在这个区域内绘制。但在这个项目中,我们决定直接在3D空间中向出现的角色身上绘制。

绘制的第一步涉及到辨识鼠标的定位。 下面,每一个被扫描的多边形,从最接近摄像头的对象开始。 如果鼠标定位从屏幕上多边形的三个顶点间滑动,会临时获得一个结构坐标从而改变当前的UV值。

下面我们添加了程序来控制当角色被渲染时保持正确的距离。 在渲染之后马上完成z轴排序,每个图形都按照离摄像头的远近进行排序。 当多边形的三向坐标被计算过之后,被扫描到得图形可以以极低的开销被定位。

在头脑风暴和功能原型设计之后,我们开发了计划中制定的直接基本绘图构件。我们没有考虑到每个角色的投影变形,所以有几次角色模型没有像预想中 一样被绘制出来——但直接将图像绘制在一个图片上显然更好玩。并且,这个新特性允许用户能真正的定制一个角色做到绝对的个性化。(见图5)

Drawing faces on one's personalized avatars

图5. 在某人的个性化角色上画脸。

从这里出发还可以学到什么

正如你看到的,我们最初想创造的个性化商务卡片仍然在开发中。 不知不觉中,我们糅合了完整的多样的个性化定制。

我们仍然有很多特性想添加。 例如,我们希望在Flickr上介绍一张有纪念意义的照片。 我们也在Apple的iphone上开发程序,让你可以不使用打印机也可以创建一个角色。 我们邀请你访问我们的网站*并创建属于你的商务卡片角色。

想学习更多AR的项目知识,请参阅Samuel Asher Rivello的文章,增强现实在摄像头和Flash上的应用*。 同样请在Flash Developer Center中学习更多的教程,和ActionScript 3的简单示例。

注意:这篇文章最早发表在日本版块的尖端咨询*栏目中。

关于作者

Keiichi Yoshikawa 从2004年开始做网络程序员,主要专注于Flash/ActionScript的开发。在2008年,他加入了BOW,除了开发网络内容之 外,Keiichi也通过联合研讨,或参与各种事件来拓展他的新方向。在Twitter上可以跟随他,地址是:twitter.com/psyark*

[转载]关于JavaScript 的事件[下]

mikel阅读(917)

[转载]关于JavaScript 的事件[下] – xugang – 博客园.

C  事件对象

i >  在IE 浏览器中,事件对象是window 对象的一个属性event 。访问方式如下:

function getEvent(){
var o_event = window.event;
}

event 对象在事件发生时被访问,执行完函数后就消失了。

ii >  在标准的DOM 中,事件对象是作为处理函数的唯一参数来获得。访问方式如下:

function getEvent(_event){
var o_event = _event
}

因此,为了兼容各种浏览器,通常采用如下方法:

function getEvent(_event){
// Firefox下参数_event 就是event对象
// IE 下获得 event对象
if(window.event)_event = window.event;
// 显示触发的事件名称
alert(_event.type);
}

下面列出了事件常用的几个属性和方法(区别):

IE 标准DOM 说明
cancelBubble cancelBubble 是否冒泡(标准DOM中只读)
stopPropagation( ) 阻止事件向上冒泡的方法
charCode 按下按键的Unicode 值
keyCode keyCode IE 中keypress 事件时表示按键的Unicode 值;
标准DOM 中keypress 事件时为0;
其余情况下,keyCode 为按键的数字代号。
srcElement target 触发事件的元素(对象源)
type type 事件的名称

此处只列出了事件成员的一小部分。

注意:

在IE 浏览器中,获得触发事件的对象源(HTML标签)是通过event 对象的srcElement 属性;

在标准的DOM 中,获得触发事件的对象源(HTML标签)是通过event 对象的target 属性;

获取事件目标的示例:

<html>
<head>
<title>事件的目标</title>
<script language=”JavaScript”>
function handle(oEvent){
//处理兼容性,获得事件对象
if(window.event) oEvent = window.event;
var oTarget;
//处理兼容性,获取事件目标
if(oEvent.srcElement)
oTarget = oEvent.srcElement;
else oTarget = oEvent.target;
//弹出目标的标记名称
alert(oTarget.tagName);
}
window.onload = function(){
var obj = document.getElementsByTagName(a)[0];
obj.onclick = handle;
}
</script>
</head>
<body>
<a href=#>获得事件源的示例</a>
</body>
</html>

D  键盘事件

事件 说明
keydown 按下键盘上的某个键触发。(一直按住某键则会持续触发
keypress 按下某个按键并产生字符时触发。(即忽略Shift 、Alt 、Ctrl 等功能键
keyup 释放某个按键时触发。

触发的顺序为:keydown —> keypress —> keyup

i >  关于keyCode属性和charCode 属性

keyCode属性: 表示键盘按键码。因此输入“a”字母和“A”字母时,都是显示键盘码 65 ;

charCode 属性:表示输入字符码。因此输入“a”字母和“A”字母时,

分别显示 97(a 字符码)和 65(A 字符码);

注意:

在标准的DOM 中:既有keyCode属性,还有charCode 属性。

但在标准的DOM 中,keypress 事件中keyCode 属性值始终为0 值;

IE 浏览器中:事件对象只有keyCode属性,没有charCode 属性。

但在IE 浏览器中,keypress 事件中的keyCode 属性值等同于标准DOM 中的charCode 属性的值;

示例代码:

<html>
<head>
<title>键盘事件</title>
<script language=”JavaScript”>
function handle(oEvent){
if(window.event){
//处理兼容性,获得事件对象
oEvent = window.event;
//设置IE的charCode值
oEvent.charCode = (oEvent.type == keypress) ? oEvent.keyCode : 0;
}
var oDiv = document.getElementById(display);
//输出测试
oDiv.innerHTML += oEvent.type // 事件名称
+ : charCode: + oEvent.charCode // 字符码 charCode
+ keyCode: + oEvent.keyCode + <br>; // 键盘码 keyCode
}
window.onload = function(){
var oTextArea = document.getElementsByTagName(textarea)[0];
oTextArea.onkeypress = handle;
oTextArea.onkeydown = handle;
}
</script>
</head>
<body>
<textarea rows=4 cols=50></textarea>
<div id=display></div>
</body>
</html>

ii >  屏蔽鼠标右键

方法一:

通过鼠标事件中,判断event 对象的button 属性值为“2”来屏蔽鼠标右键。虽然这种方法在IE 浏览器中有效,但在标准的DOM 中无效。并且,鼠标事件中,button 属性的值在IE 浏览器和标准的DOM 中大部分都不相同!

方法二:

其实,点击鼠标右键会触发右键菜单contextmenu 事件。

所以,屏蔽鼠标右键最有效的办法就是屏蔽document 对象的contextmenu 事件。

代码如下:

<html>
<head>
<title>屏蔽鼠标右键</title>
<script language=”javascript”>
function block(oEvent){
if(window.event){
oEvent = window.event;
// IE 取消默认事件
oEvent.returnValue = false;
}
else
// Firefox 取消默认事件
oEvent.preventDefault();
}
// 右键菜单事件
document.oncontextmenu = block;
</script>
</head>
<body>
<p>屏蔽鼠标右键</p>
</body>
</html>

IE 浏览器是通过returnValue 属性屏蔽右键菜单;

标准DOM 是通过preventDefault( ) 方法屏蔽右键菜单;

iii > 自定义鼠标右键菜单

代码如下:

<html>
<head>
<title> demo </title>
<meta name=Author content=xugang />
<script type=”text/javascript”>
<!–
// 一、屏蔽系统右键菜单
window.document.oncontextmenu = function(_event){
if (window.event){
_event = window.event;
window.event.returnValue=false;
window.event.cancelBubble=true;
}
else _event.preventDefault();
}
//二、添加自定义右键菜单
window.document.onmouseup = function(_event)
{
var myDIV = document.getElementById(myDIV);
// 浏览器兼容性
if (window.event)_event = window.event;
// 鼠标右键
if(_event.button == 2)
{
// _event.clientX 获得鼠标当前的 X 坐标
/* 注意:
_event.clientX 的值在标准的DOM 中“只读”
myDIV.style.pixelLeft 不是标准的DOM 属性
*/
myDIV.style.left = _event.clientX;
myDIV.style.top = _event.clientY;
myDIV.style.display = block;
}
// 不是鼠标右键
else myDIV.style.display = none;
}
//–>
</script>
</head>
<body>
<!– 我的右键菜单 –>
<div id=myDIV style=position:absolute; height:180px; width:200px;
background-color:#CCCCCC; display:none;>
<a href=http://xugang.cnblogs.com target=_blank>xugang</a>
</div>
</body>
</html>

在IE 浏览器和标准DOM 下兼容。

[转载]关于JavaScript 的事件[上]

mikel阅读(1079)

[转载]关于JavaScript 的事件[上] – xugang – 博客园.

A  事件流(event  flow )

事件模型分为两种:冒泡型事件、捕获型事件。

冒泡型(dubbed  bubbling )事件:指事件按照从最精确的对象到最不精确的对象的顺序逐一触发。

捕获型(event  capturing )事件:它与冒泡型事件相反,指事件按照从最不精确的对象到最精确的对象的顺序逐一触发。

冒泡事件和捕获事件的执行顺序

捕获型事件也被称作自顶向下(DOM层次)的事件模型。

由于IE 浏览器不支持捕获型事件,因此并没有被广泛应用。

B  事件监听

i >  通用监听方法

示例一:

<p onclick=alert(‘点击了’);>Click Me</p>

示例二:

<html>
<head>
<title> demo </title>
<meta name=Author content=xugang />
<script type=”text/JavaScript”>
// 在onload 事件中添加所有标签的事件
window.onload = function(){
// 找到对象
var o_p = document.getElementById(myp);
// 添加对象的onclick 事件
o_p.onclick = function(){
alert(我被点击了);
}
}
</script>
</head>
<body>
<p id=myp>Click Me</p>
</body>
</html>

此代码实现了结构与行为的分离。

给浏览器添加监听方法,分为两种:IE 中的监听方法、标准DOM 的监听方法。

ii >  IE 中的监听方法

在IE 浏览器中,每个元素都有两个方法来处理事件的监听。分别是:attachEvent( )detachEvent( )

附加事件方法:[object].attachEvent(“事件名”,方法名);

分离事件方法:[object].detachEvent(“事件名”,方法名);

如:o_p.detachEvent(“onclick”,click_A);

示例:

<html>
<head>
<title> demo </title>
<meta name=Author content=xugang />
<script type=”text/JavaScript”>
<!–
function click_A(){
alert(click_A);
//删除监听函数
o_p.detachEvent(onclick,click_B);
}
function click_B(){
alert(click_B, 我只调用一次。);
}
var o_p;
window.onload = function(){
o_p = document.getElementById(myP);
// 添加监听函数 click_A
o_p.attachEvent(onclick,click_A);
// 添加监听函数 click_B
o_p.attachEvent(onclick,click_B);
}
//–>
</script>
</head>
<body>
<p id=myP>Click Me</p>
</body>
</html>

注意:

●  使用这种方式可以为同一元素添加多个监听函数;

●  在IE 浏览器中,函数的执行顺序与函数的添加顺序相反;

●  在IE 浏览器中,虽然函数有先后执行顺序,但都会同时调用

iii >  标准DOM 的监听方法

在使用标准DOM 的浏览器中,每个元素也有两个方法来处理事件的监听。分别是:addEventListener( )removeEventListener( )

添加事件监听方法:[object].addEventListener(“事件名”,方法名,事件模型 );

移除事件监听方法:[object].removeEventListener(“事件名”,方法名,事件模型 );

注意:

这里的“事件名”不能带 on ,如:click(如果是onclick 则错误!)

“事件模型”为boolean 类型,通常设置为 false ,即“冒泡型”事件。(如果是true 则为“捕获型”事件)

示例:

<html>
<head>
<title> demo </title>
<meta name=Author content=xugang />
<script type=”text/JavaScript”>
<!–
function click_A(){
alert(click_A);
//删除监听函数
o_p.removeEventListener(click,click_B,false);
}
function click_B(){
alert(被click_A删除, 一次都不能调用。);
}
var o_p;
window.onload = function(){
o_p = document.getElementById(myP);
// 添加监听函数 click_A
o_p.addEventListener(click,click_A,false);
// 添加监听函数 click_B
o_p.addEventListener(click,click_B,false);
}
//–>
</script>
</head>
<body>
<p id=myP>Click Me</p>
</body>
</html>

在Firefox 下运行通过,在IE 下报错。

注意:

●  使用这种方式同样可以为同一元素添加多个监听函数;

●  在Firefox 浏览器中,函数的执行顺序与函数的添加顺序一致(Firefox 与IE 正好相反);

●  在Firefox 浏览器中,这种方式添加的函数是执行外一个再执行另一个(逐个执行);

[转载]NHibernate3.0剖析:Query篇之NHibernate.Linq标准查询

mikel阅读(992)

[转载]NHibernate3.0剖析:Query篇之NHibernate.Linq标准查询 – YJingLee’s Blog – 博客园.

本节内容

系列引入

NHibernate3.0剖析系列分别从Configuration篇、Mapping篇、Query篇、Session策略篇、应用篇等方面全面揭示NHibernate3.0新特性和应用及其各种应用程序的集成,基于NHibernte3.0版本。如果你还不熟悉NHibernate,可以快速阅读NHibernate之旅系列文章导航系列入门,如果你已经在用NHibernate了,那么请跟上NHibernate3.0剖析系列吧。

NHibernate.Linq概述

NHibernate.Linq基于HQL AST分析器的Linq Provider,由Steve Strong贡献者开发的,底层使用第三方Re-Linq开源框架。所以NHibernate3.0多了一个必需程序集:Remotion.Data.Linq.dll。

注意:在之前NHibernate版本中并不存在Linq功能,Ayende Rahien贡献者为NHibernate2.1.0GA和NHibernate2.1.2GA版本设计第三方NHiberante.Linq.dll(对应为NHibernate.Linq-1.0.0.GA-bin.zipNHibernate.Linq-2.1.2-GA-Bin.zip)(目前已经停止了维护),它是基于Criteria API的Linq Provider,主要功能是将简单的Linq表达式转化为Criteria API,由于Criteria API的功能有限,所以存在很多天生的不足(联接和子查询不支持)。如果使用NHibernate2.1.0GA或者NHibernate2.1.2GA版本可以下载使用NHiberante.Linq.dll,在这里不作介绍。

下面看看NHibernate提供的全新的NHibernate.Linq查询。

我们使用ISession接口的Query<T>()扩展方法创建一个NHibernate.Linq查询。

Linq-Query

首先需要using NHibernate.Linq命名空间,然后使用ISession.Query<T>()获得IQueryable<T>,我 们对其做一些延迟操作(例如where、orderby等),最后使用不延迟的操作(例如ToList()、Count()、 FirstOrDefault())返回需要的结果。

注意,NHibernate.Linq查询将Linq运算符转换为HQL,有些Linq运算符本身是专门处理集合的,而SQL主要是在处理无序值 集。所以NHibernate.Linq查询肯定不需要支持这些专门处理集合的运算符,例如Except、Intersect、转换运算符、生成运算符 等。

下面列举所有Linq运算符和说明,并列举了一些简单的NHibernate.Linq查询,我仅仅对单一对象User对象操作:

//Code Snippets Copyright http://lyj.cnblogs.com/
public class User
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

标准查询运算符

1.基本形式

使用ISession的Query<User>(),然后ToList()查询出所有的User对象。

//Code Snippets Copyright http://lyj.cnblogs.com/
var basicquery = (from user in session.Query<User>()
                  select user).ToList();

var basicquery2 = session.Query<User>().ToList();

2.限制运算符

  • Where:筛选序列中的项目

说明:对属性值一些筛选,筛选属性支持组件、枚举、各种关联、支持基本类型的方法。例如Int等类型的等于、大于、小于,不等于;String类型 的StartsWith、EndsWith、Contains、Equals、ToLower、ToLowerInvariant、ToUpper、 ToUpperInvariant、Substring、IndexOf等;DateTime的Year、Date、Month等。也可以在Where运 算符中使用聚合操作符的子查询。

//Code Snippets Copyright http://lyj.cnblogs.com/
var restrictionquery = (from user in session.Query<User>()
                  where user.Name == "李永京"
                  select user).ToList();

var restrictionquery2 = session.Query<User>().Where(o => o.Name == "李永京").ToList();

3.投影运算符

  • Select:创建部分序列的投影
  • SelectMany:创建部分序列的一对多投影

说明:Select运算符对于大多数操作都支持,也支持子查询,但是不支持嵌套Select。SelectMany不支持。

//Code Snippets Copyright http://lyj.cnblogs.com/
var selectAnonymousquery = (from user in session.Query<User>()
                            select new {user.Name, Age = user.Age}).ToList();

var selectAnonymousquery2 = session.Query<User>()
    .Select(o => new {o.Name, Age = o.Age})
    .ToList();

4.分区运算符

  • Skip:返回跳过指定数目项目的序列
  • SkipWhile:返回跳过不满足表达式项目的序列
  • Take:返回具有指定数目项目的序列
  • TakeWhile:返回具有满足表达式项目的序列

说明:不支持SkipWhile和TakeWhile。不支持连写多个Take或者Skip。

//Code Snippets Copyright http://lyj.cnblogs.com/
var partitioningquery = (from user in session.Query<User>()
              select user).Take(2).Skip(2).ToList();

var partitioningquery2 = session.Query<User>().Take(2).Skip(2).ToList();

5.排序运算符

  • OrderBy:以升序按值排列序列
  • OrderByDescending:以降序按值排列序列
  • ThenBy:升序排列已排序的序列
  • ThenByDescending:降序排列已排序的序列
  • Reverse:颠倒序列中项目的顺序(用于操作集合)

说明:排序运算符不支持子查询。

//Code Snippets Copyright http://lyj.cnblogs.com/
var orderingquery = (from user in session.Query<User>()
               orderby user.Id descending, user.Name ascending
               select user).ToList();

var orderingquery2 = session.Query<User>()
    .OrderByDescending(o => o.Id).OrderBy(o=>o.Name).ToList();

6.分组运算符

  • GroupBy:按指定分组方法对序列中的项目进行分组

例如下面查询:

//Code Snippets Copyright http://lyj.cnblogs.com/
var groupquery = (from user in session.Query<User>()
                  group user by user.Name
                      into g
                      select new
                      {
                          g.Key,
                          Age = g.Sum(p => p.Age)
                      }).ToList();
var groupquery2 = session.Query<User>().GroupBy(o => o.Name)
    .Select(o =>new { o.Key, Age = o.Sum(p => p.Age)}).ToList();

7.设置运算符

  • Distinct:返回无重复项目的序列
  • Except:返回代表两个序列差集的序列(用于操作集合)
  • Intersect:返回代表两个序列交集的序列(用于操作集合)
  • Union:返回代表两个序列交集的序列(用于操作集合)

目前支持Distinct:

//Code Snippets Copyright http://lyj.cnblogs.com/
var distinctquery = session.Query<User>().Distinct().ToList();

8.转换运算符(用于操作集合)

  • Cast:将序列中的元素转换成指定类型
  • OfType:筛选序列中指定类型的元素
  • ToArray:从序列返回一个数组
  • ToDictionary:从序列返回一个字典
  • ToList:从序列返回一个列表
  • ToLookup:从序列返回一个查询
  • ToSequence:返回一个IEnumerable序列

NHibernate.Linq不需要支持。

9.元素运算符

  • DefaultIfEmpty:为空序列创建默认元素(用于操作集合)
  • ElementAt:返回序列中指定索引的元素(用于操作集合)
  • ElementAtOrDefault:返回序列中指定索引的元素,或者如果索引超出范围,则返回默认值(用于操作集合)
  • First:返回序列中的第一个元素
  • FirstOrDefault:返回序列中的第一个元素,或者如果未找到元素,则返回默认值
  • Last:返回序列中的最后一个元素(用于操作集合)
  • LastOrDefault:返回序列中的最后一个元素,或者如果未找到元素,则返回默认值(用于操作集合)
  • Single:返回序列中的单个元素
  • SingleOrDefault:返回序列中的单个元素,或者如果未找到元素,则返回默认值

例如下面例子:

//Code Snippets Copyright http://lyj.cnblogs.com/
var firstquery = session.Query<User>().First(u => u.Name == "李永京");
var firstOrDefaultquery = session.Query<User>().FirstOrDefault(u => u.Name == "李永京");

var singlequery = session.Query<User>().Single(u => u.Name == "李永京");
var singleOrDefaultquery = session.Query<User>().SingleOrDefault(u => u.Name == "李永京");

10.生成运算符(用于操作集合)

  • Empty:生成一个空序列
  • Range:生成一个指定范围的序列
  • Repeat:通过将某个项目重复指定次数来生成一个序列

NHibernate.Linq不需要支持。

11.限定符

  • All:确定序列中的所有项目是否满足某个条件
  • Any:确定序列中是否有任何项目满足条件
  • Contains:确定序列是否包含指定项目

仅写下Any示例:

//Code Snippets Copyright http://lyj.cnblogs.com/
var anyquery = session.Query<User>().Any();//就是取任意一个

12.聚合运算符

  • Aggregate:对序列执行一个自定义方法
  • Average:计算数值序列的平均值
  • Count:返回序列中的项目数(整数)
  • LongCount:返回序列中的项目数(长型)
  • Min:查找数字序列中的最小数
  • Max:查找数字序列中的最大数
  • Sum:汇总序列中的数字

一些简单例子:

//Code Snippets Copyright http://lyj.cnblogs.com/
var aggregatequery = session.Query<User>()
    .Where(o => o.Name.Contains("李永京"))
    .Select(o => o.Id)
    .Aggregate(new StringBuilder(), (sb, id) => sb.Append(id).Append(","));
var average = session.Query<User>().Average(u => u.Age);
var countquery = session.Query<User>().Count();
var longCountquery = session.Query<User>().LongCount();
var minquery = session.Query<User>().Min(u => u.Age);
var maxquery = session.Query<User>().Max(u => u.Age);
var sumquery = session.Query<User>().Sum(u => u.Age);

13.连接运算符

  • Concat:将两个序列连成一个序列

目前还未支持!

14.联接运算符

  • GroupJoin:通过归组将两个序列联接在一起
  • Join:将两个序列从内部联接起来

涉及对象关联操作,以后单独介绍。

结语

这篇利用单一对象学习下NHibernate.Linq最基本的标准查询,没有涉及多个对象操作,比较简单,目的让大家先熟悉一下 NHibernate.Linq查询。接下来的继续学习NHibernate.Linq。主要有NHibernate.Linq复杂标准查询、 NHibernate.Linq增强查询、自定义NHibernate.Linq查询及其在项目中的应用。

希望本文对你有所帮助。

[转载]Saas模式数据库层数据架构以及数据删除处理

mikel阅读(956)

[转载]Saas模式数据库层数据架构以及数据删除处理 – 展翅高飞 – 博客园.

Saas 模式,所有的模式和服务可以为多个企业服务,每个企业都有自己的一套数据,在同一环境,管理相互隔离的数据,保证数据安全和可用性,是多企业数据架构解决的核心问题!

公司的产品涉及的客户公司有几千家,以前的数据库设计是每个客户独立的一个数据库,通过一个目录数据库独立的访问各家企业!在应用服务器配置不同的连接 池。不过问题来了,虽然不同的客户,数据物理分离,安全性好,但是硬件成本太高,数据库连接效率不高,因此采用了单一数据源,利用muti- Schema,不同企业通过Schema 的不同,一一对应,这样共享数据源与连接池,效率上来说更高,成本更低!不过整个数据库架构都需要从新设计,可喜的是产品架构设计独立性,扩展性都兼顾 了,只需要修改了数据库层,添加新的代码规范,系统很好的过渡到Muti-Schema!

在整个框架过渡中,对于Muti-Schema存在的问题是数据库的备份,导入,删除!数据库的备份费时,所有的公司只能够同时备份,不能够单独单个企业 的处理!数据的导入大量的应用了SSIS,产品的升级,从旧版本升级到新版本,海量数据的迁移,充分利用了SSIS,做了一个海量数据迁移工具,比起 DTS,充分利用SSIS重写一个迁移工具,针对不同的业务,充分发掘SSIS的优势,与DTS不在一个级别!

数据库的删除,由于不是在单一的数据库(单一的数据库直接delete)!删除数据库比较麻烦,要删除一个公司的数据库,由于所有公司客户的数据在同一数 据库,公司客户是针对Schema 来区分的,所以一个解决方案是通过删除对应公司的Schema 以及Shcema的所有对象!通过脚本,写了一个存储过程,首先找出外键,以及约束,生成脚本插入到一个零时表,按顺序的删除外键,约束,再 view,proc,trigger 相关对象,从而达到删除相关的公司客户数据,而不影响其它的公司客户的数据!

下面是具体的Muti-Schema数据库删除脚本:

1 ALTER PROCEDURE [dbo].[DropSchemanameAndAllObjects]
2 (
3 @SchemaName varchar(100)
4 )
5 AS
6 BEGIN
7 declare @SQL varchar(4000)
8 declare @msg varchar(500)
9
10 Create temp table ,insert into statement for drop all objects
11 IF OBJECT_ID(tempdb..#DropTableCode) IS NOT NULL DROP TABLE #DropTableCode
12 CREATE TABLE #DropTableCode
13 (
14 ID int identity(1,1),
15 SQLstatement varchar(1000)
16 )
17
18 drop all the foreign keys that reference a PK in the target schema
19 SELECT @SQL =
20 select
21 ALTER TABLE +SCHEMA_NAME(fk.schema_id)+.+OBJECT_NAME(fk.parent_object_id)+ DROP CONSTRAINT + fk.name
22 FROM sys.foreign_keys fk
23 join sys.tables t on t.object_id = fk.referenced_object_id
24 where t.schema_id = schema_id(”’ + @SchemaName+”’)
25 and fk.schema_id <> t.schema_id
26 order by fk.name desc
27 INSERT INTO #DropTableCode
28 EXEC (@SQL)
29
30 drop all default constraints, check constraints and Foreign Keys
31 SELECT @SQL =
32 SELECT
33 ALTER TABLE +schema_name(t.schema_id)+.+OBJECT_NAME(fk.parent_object_id)+ DROP CONSTRAINT + fk.[Name]
34 FROM sys.objects fk
35 join sys.tables t on t.object_id = fk.parent_object_id
36 where t.schema_id = schema_id(”’ + @SchemaName+”’)
37 and fk.type IN (D, C, F)
38 INSERT INTO #DropTableCode
39 EXEC (@SQL)
40
41 drop all other objects in order such as table,view,procedure .
42 SELECT @SQL =
43 SELECT
44 CASE WHEN SO.type=PK THEN ALTER TABLE +SCHEMA_NAME(SO.schema_id)+.+OBJECT_NAME(SO.parent_object_id)+ DROP CONSTRAINT + SO.name
45 WHEN SO.type=U THEN DROP TABLE +SCHEMA_NAME(SO.schema_id)+.+ SO.[Name]
46 WHEN SO.type=V THEN DROP VIEW +SCHEMA_NAME(SO.schema_id)+.+ SO.[Name]
47 WHEN SO.type=P THEN DROP PROCEDURE +SCHEMA_NAME(SO.schema_id)+.+ SO.[Name]
48 WHEN SO.type=TR THEN DROP TRIGGER +SCHEMA_NAME(SO.schema_id)+.+ SO.[Name]
49 WHEN SO.type  IN (FN, TF,IF,FS,FT) THEN DROP FUNCTION +SCHEMA_NAME(SO.schema_id)+.+ SO.[Name]
50 END
51 FROM SYS.OBJECTS SO
52 WHERE SO.schema_id = schema_id(”’+ @SchemaName +”’)
53 AND SO.type IN (PK, FN, TF, TR, V, U, P)
54 ORDER BY CASE WHEN type = PK THEN 1
55 WHEN type in (FN, TF, P,IF,FS,FT) THEN 2
56 WHEN type = TR THEN 3
57 WHEN type = V THEN 4
58 WHEN type = U THEN 5
59 ELSE 6
60 END
61 INSERT INTO #DropTableCode
62 EXEC (@SQL)
63
64 drop all userdefined data types
65 SELECT @SQL=
66 SELECT
67 DROP TYPE +”’+@SchemaName+”’+.+O.NAME
68 FROM sys.Types o
69 where is_user_defined=1
70 and schema_id=schema_id(”’+@SchemaName+”’)
71 INSERT INTO #DropTableCode
72 EXEC(@SQL)
73
74 create a cursor for temp table #DropTableCode and fetch each statement to drop
75
76 DECLARE @statement varchar(1000)
77 DECLARE statement_cursor CURSOR
78 FOR SELECT SQLStatement
79 FROM #DropTableCode
80 ORDER BY ID ASC
81
82 OPEN statement_cursor
83 FETCH statement_cursor INTO @statement
84 WHILE (@@FETCH_STATUS = 0)
85 BEGIN
86 BEGIN
87 PRINT (@statement)
88 EXEC(@statement)
89 END
90 FETCH statement_cursor INTO @statement
91 END
92 CLOSE statement_cursor
93
94 DEALLOCATE statement_cursor
95 drop schema name
96 iF UPPER(@SchemaName) = DBO PRINT (DBO Will not drop)
97 ELSE
98 BEGIN
99 PRINT (DROP SCHEMA +@SchemaName)
100 EXEC (DROP SCHEMA +@SchemaName)
101 END
102 PRINT ——- ALL – DONE ——-
103 END

上面的Proc对于 Muti-Schema数据库的设计删除以及海量数据的迁移过程中起到了很大的作用!应为在数据迁移的工程中,迁移工具要做很多工作,比如版本比较,迁移 之后的数据比较是否正确,在开发迁移工具的Test过程中,充分发挥了威力!版本比较,迁移之后数据不一致都需要删除从做,所以上面的Proc起到了很大 的帮助!

数据迁移,海量挖掘,从DTS到SSIS!不断继续深入着!

[转载]C#代码规范 程序员必备的秘笈

mikel阅读(793)

[转载]C#代码规范 程序员必备的秘笈 – javin的博客 – 博客园.

1. 引言

本文是一套面向C# programmer 和C# developer 进行开发所应遵循的开发规范。

按照此规范来开发C#程序可带来以下益处:

·         代码的编写保持一致性,

·         提高代码的可读性和可维护性,

·         在团队开发一个项目的情况下,程序员之间可代码共享

·         易于代码的回顾,

本规范是初版,只适用于一般情况的通用规范,并不能覆盖所有的情况。

2. 文件组织

2.1 C# 源文件

类名或文件名要简短,不要超过2000LOC,将代码分割开,使结构清晰。将每个类放在一个单独的文件中,使用类名来命名文件名(当然扩展名是.cs)。这种约定会使大家工作更简单。

2.2 目录设计

为每一个命名空间创建一个目录。(用MyProject/TestSuite/TestTier作为MyProject.TestSuite.TestTier的路径,而不用带点的命名空间名做路径)这样可以更容易地将命名空间映射到目录层次划分。

3. 缩进

3.1 换行

当一个表达式超过一行时,根据这些通用原则进行处理:

·         在逗号后换行。

·         在操作符后换行。

·         在高层换行而不要在低层处换行。

·         折行后对齐上一行语句同一层的表达式起始位置。

方法调用换行示例:

longMethodCall(expr1, expr2,

expr3, expr4, expr5);

算术表达式换行示例:

推荐:

var = a * b / (c – g + f) +

4 * z;

不好的格式——应避免:

var = a * b / (c – g +

f) + 4 * z;

推荐使用第一种方法,因为是在括号表达式之外折行(高层次折行原则)。注意要用制表符到缩进的位置,然后用用空格到折行的位置。在我们的例子中是:

> var = a * b / (c – g + f) +

> ……4 * z;

‘>’表示是制表符,’.’表示是空格符。(制表符后是空白是用制表符缩进)。一个好的编码习惯就是在所用的编辑器中显示制表符和空格符。

3.2 空白

利用空格进行缩进从未有过统一的标准。一些人喜欢用两个空格,一些人喜欢用四个空格而还有一些人喜欢用八个空格,甚至有的人喜欢用更多的空格。好的做法是用制表符。制表符有一些优点:

·         每个人都可以设置他们自己喜欢的缩进层级。

·         它仅仅是1个字符而不是2,4,8等等,因此它将减少输入(甚至因为自动缩进,有时你不得不手工设置缩进或取消设置,等等诸如此类的操作)。

·         如果你想增加或减少缩进,可以标记一块,使用Tab增加缩进层级而用Shift-Tab减少缩进层级。这几乎对于任何文本编辑器都是适用的。

这里,我们定义制表符为标准缩进符。

不要用空格缩进—用制表符!

4. 注释

4.1 块注释

块注释通常应该是被避免的。推荐使用///注释作为C#的标准声明。如果希望用块注释时你应该用以下风格:

/* Line 1

* Line 2

* Line 3

*/

因为样可以为读者将注释块与代码块区分开。虽然并不提倡使用C风格的单行注释,但你仍然可以使用。一旦用这种方式,那么在注释行后应有断行,因为很难看清在同一行中前面有注释的代码:

/* blah blah blah */

块注释在极少情况下是有用的。通常块注释用于注释掉大的代码段。

4.2 单行注释

你应该用//注释风格“注释掉”代码(快捷键,Alt+/)。它也可以被用于代码的注释部分。

单行注释被用于代码说明时必须缩进到相应的编进层级。注释掉的代码应该放在第一行被注释掉以使注释掉的代码更容易看清。

一条经验,注释的长度不应该超过被解释代码的长度太长,因为这表示代码过于复杂,有潜在的bug。

4.3 文件注释

在.net 框架,Microsoft 已经介绍了一个基于XML 注释的文件。这些文件是包括XML 标签的正规的单行的C#注释。他们遵循单行注释的模式:

/// <summary>

/// This class…

/// </summary>

多行XML 注释遵循这种模式:

/// <exception cref=”BogusException”>

/// This exception gets thrown as soon as a

/// Bogus flag gets set.

/// </exception>

为了被认作是XML注释行,所有的行都必须用三个反斜线开始。标签有以下两类:

·         文件说明项

·         格式/参考

第一类包括像<summary>, <param> or <exception>的标签。描述一个程序的API元素的这些文档说明项必须写清楚以方便其他程序员。如上面的多行注释示例所示,这些标签 通常带有名称或cref属性。编译器会检查这些属性,所以它们必须是有效、正确的。第二类用诸如<code>, <list> or <para>标签,用于控制备注说明的布局。

文件可以用‘文件’菜单中的‘创建’菜单产生。文件以HTML格式产生。

5. 声明

5.1 每行的声明数

推荐每行只有一个声明,因为它可以方便注释。

int level; // indentation level

int size; // size of table

当声明变量时,不要把多个变量或不同类型的变量放在同一行,例如:

int a, b; //What is ‘a’? What does ‘b’ stand for?

上面的例子也显示了变量名不明显的缺陷。当命名变量时要清晰。

5.2 初始化

局部变量一旦被声明就要初始化。例如:

string name = myObject.Name;

int val = time.Hours;

注意:如果你初始化一个dialog,设计使用using语句:

using (OpenFileDialog openFileDialog = new OpenFileDialog()) {

}

5.3 类和接口声明

当编写C#类和接口时,应遵循以下格式化规则:

·         在方法名和圆括号“(”开始它的参数列表之间不要使用空格。

·         在声明语句的下一行以大括号”{“标志开始。

·         以”}”结束,通过它自身的缩进与相应的开始标志匹配。

例如:

Class MySample : MyClass, IMyInterface

{

int myInt;

public MySample(int myInt)

{

this.myInt = myInt ;

}

void Inc()

{

++myInt;

}

void EmptyMethod()

{

}

}

对于一个大括号的位置参考10.1部分。

6. 语句

6.1 简单语句

每行都应该只包含一条语句。

6.2 返回语句

一个返回语句不要用最外围圆括号。不用:

return (n * (n + 1) / 2);

用: return n * (n + 1) / 2;

6.3 If, if-else, if else-if else 语句

if, if-else and if else-if else 语句看起来应该像这样:

if (condition) {

DoSomething();

}

if (condition) {

DoSomething();

} else {

DoSomethingOther();

}

if (condition) {

DoSomething();

} else if (condition) {

DoSomethingOther();

} else {

DoSomethingOtherAgain();

}

6.4 for / foreach 语句

一个for语句应该如下形式:

for (int i = 0; i < 5; ++i) {

}

或者放置一行(考虑用一个while语句代替)

for (initialization; condition; update) ;

foreach语句应该像下面所示 :

foreach (int i in IntList) {

}

注意:在一个循环中,即使只有一个语句通常也用括弧括起来。

6.5 While/do-while 语句

一个while语句应该写成如下形式:

while (condition) {

}

一个空while语句应该是以下格式:

while (condition) ;

一个do-while语句应该是如下格式:

do

{

} while (condition);

6.6 Switch 语句

一个switch语句应该如下格式:

switch (condition) {

case A:

break;

case B:

break;

default:

break;

}

6.7 Try-catch 语句

一个try-catch statement语句应该遵循以下格式:

try {

} catch (Exception) {}

or

try {

} catch (Exception e) {

}

or

try {

} catch (Exception e) {

} finally {

}

7. 空白

7.1 空行

空行提高可读性。它们分开那些逻辑上自身相关联的代码块。两行空格行应该用于以下之间:

·         一个源文件的逻辑段。

·         类和接口定义(每个文件只定义一个类或接口以避免这种情况)。

一个空格行应该总是被用于以下之间:

·         方法

·         属性

·         一个方法中的局部变量和它的第一条语句

·         一个方法中的逻辑段为了提高可读性。注意空白行必须被缩进因为它们包括一条语句这使得插入这些行更容易。

7.2 内部空格

在一个逗号或一个分号之后应该由一个空格,例如:

TestMethod(a, b, c); 不要用: TestMethod(a,b,c)

TestMethod( a, b, c );

单个空格包围操作符(除了像加的一元操作符和逻辑非),例:

a = b; // don’t use a=b;

for (int i = 0; i < 10; ++i) // don’t use for (int i=0; i<10; ++i)

// or

// for(int i=0;i<10;++i)

7.3 表格格式化

行的一个逻辑块应该作为一个表格被格式化:

string name = “Mr. Ed”;

int myValue = 5;

Test aTest = Test.TestYou;

对于表格的格式化用空格而不用制表符因为在某些制表符缩进设置会使表格格式化看起来是很奇怪。

8. 命名习惯

8.1 大写格式

8.1.1 Pascal Casing

习惯大写每个单词的第一个字母(就像在TestCounter)。

8.1.2 Camel Casing

习惯除了第一个单词外大写每个单词的第一个字母例如testCounter。

8.1.3 全大写情况

对于只有一两个字符缩写组成的标识符才用全大写的情况。有三个或更多个字符组成的标识符应该用Pascal情况代替。例如:

public class Math

{

public const PI = …

public const E = …

public const feigenBaumNumber = …

}

8.2. 命名指导方针

通常根据指导方针在名字和命名内用低线字符对Hungarian 符号来说被认为是坏习惯。

Hungarian 符号是一组应用于命名来映射变量类型的前缀和后缀。这种命名风格在早期的Windows程序中被广泛应用,但现在被取消了至少不提倡了。如果你遵循这个指南用Hungarian 符号是不允许的。

但要记住一个好的变量名描述了语义而不失类型。

对于这个规则有个例外就是GUI编码。包括像按钮(buttton)的GUI元素,所有领域和变量名都应该带有它们类型名的后缀不是缩写。例如:

System.Windows.Forms.Button cancelButton;

System.Windows.Forms.TextBox nameTextBox;

8.2.1 类命名指导方针

·         类命名必须是名词或名词短语。

·         UsePascal 情况参考8.1.1

·         不要用任何类前缀

8.2.2 接口命名指导方针Guidelines

·         用可以描述行为的名词或名词短语或形容词命名接口。(例如IComponent 或 IEnumberable)

·         用Pascal情况(参考8.1.1)

·         用I作为名字的前缀,它应该紧跟一个大写字母(接口名的第一个字母)

8.2.3 枚举命名指导方针

·         用Pascal情况命名枚举值名字和类型名字

·         枚举类型和枚举值不要前缀

·         对于枚举用单一名字

·         对于位领域用复数名字

8.2.4 只读和常量命名

·         用名词,名词短语或名词的缩写命名静态领域

·         使用Pascal 情况(参考8.1.1)

8.2.5 参数/非常量领域命名

·         一定要用描述性名字,应该能够足够表现变量的意义和它的类型。但一个好的名字应该基于参数的意义。

·         使用Camel情况(参考8.1.2)

8.2.6 变量命名

·         计数变量当用在琐碎的计数循环式更适宜叫i, j, k, l, m, n。(参考10.2例如对全局计数的更智能命名等等)—

·         使用Camel情况(参考8.1.2)

8.2.7 方法命名

·         用动词或动词短语命名方法。

·         使用Pascal(参考8.1.2)

8.2.8 属性命名

·         用名词或名词短语命名属性

·         使用Pascal 情况(参考8.1.2)

·         考虑用与其类型相同的名字命名一个属性

8.2.9 事件命名

·         用事件处理器后缀命名事件处理器

·         用sender 和 e命名两个参数

·         使用Pascal情况(参考8.1.1)

·         用EventArgs 后缀命名事件参数

·         用现在和过去时态命名有前缀和复制概念的事件名字。

·         考虑用一个动词命名事件。

8.2.10 大写总结

Type

Case

Notes

Class / Struct

Pascal Casing

Interface

Pascal Casing

Starts with I

Enum values

Pascal Casing

Enum type

Pascal Casing

Events

Pascal Casing

Exception class

Pascal Casing

End with Exception

public Fields

Pascal Casing

Methods

Pascal Casing

Namespace

Pascal Casing

Property

Pascal Casing

Protected/private Fields

Camel Casing

Parameters

Camel Casing

9. 编程习惯

9.1 可见性

不要任何公共实例或类变量,让它们为私有的。对于私有成员最好不用“private”作修饰语什么都不写。私有是默认情况,每个C#程序员都应该知道这一点。

用属性代替。你可以用公共静态(或常量)对于这个规则是以例外,带它不应该是规则。

9.2 没有“幻”数

不要用幻数,也就是在源代码中直接用常数值。替代这些后者以防变化(比方说,你的应用程序可以处理3540用户代替427你的代码在50行中通过分散25000LOC)是错误和没有收益的。声明一个带有数的常量来代替:

public class MyMath

{

public const double PI = 3.14159…

}

10. 编码举例

10.1 Brace placement example

namespace ShowMeTheBracket

{

public enum Test {

TestMe,

TestYou

}

public class TestMeClass

{

Test test;

public Test Test {

get {

return test;

}

set {

test = value;

}

}

void DoSomething()

{

if (test == Test.TestMe) {

//…stuff gets done

} else {

//…other stuff gets done

}

}

}

}

括弧应该在以下情况之后以新行开始:

·         命名空间声明(注意这在0.3版本中是新添的与0.2版本不同)

·         类/接口/结构声明

·         方法声明

10.2 变量命名举例

代替:

for (int i = 1; i < num; ++i) {

meetsCriteria[i] = true;

}

for (int i = 2; i < num / 2; ++i) {

int j = i + i;

while (j <= num) {

meetsCriteria[j] = false;

j += i;

}

}

for (int i = 0; i < num; ++i) {

if (meetsCriteria[i]) {

Console.WriteLine(i + ” meets criteria”);

}

}

try intelligent naming :

for (int primeCandidate = 1; primeCandidate < num; ++primeCandidate)

{

isPrime[primeCandidate] = true;

}

for (int factor = 2; factor < num / 2; ++factor) {

int factorableNumber = factor + factor;

while (factorableNumber <= num) {

isPrime[factorableNumber] = false;

factorableNumber += factor;

}

}

for (int primeCandidate = 0; primeCandidate < num; ++primeCandidate)

{

if (isPrime[primeCandidate]) {

Console.WriteLine(primeCandidate + ” is prime.”);

}

}

注意:索引变量通常叫i, j, k 等等。但Note: Indexer variables generally should be called i, j, k etc. But 万一像这样,使得重新考虑这个原则更有意义。一般来说,当同一个计数器或索引器被重用,给它们有意义的名字。

[转载]Access使用参数方法更新数据库的问题

mikel阅读(1020)

[转载]Access使用参数方法更新数据库的问题 – Monolight – 博客园.

在昨天下午就遇到了这个问题,但是调试一直都能成功,但是数据却总是没有更新。在网上搜索了下,这方面的东西还是挺少的,可能是因为Access数据库用得少,或者是在更新的时候使大多人不用参数方式的原因吧,今天我把我的经历记录一下吧。以后也可以做参考。

原来有误的代码:

   1:  StringBuilder strSql = new StringBuilder();
   2:  strSql.Append("update news_Table set ");
   3:  strSql.Append("NewsTitle=@NewsTitle,");
   4:  strSql.Append("NewsContent=@NewsContent,");
   5:  strSql.Append("NewsReporter=@NewsReporter");
   6:  
   7:  strSql.Append(" where NewsID=@NewsID ");
   8:  OleDbParameter[] parameters = {
   9:          new OleDbParameter("@NewsID", OleDbType.Integer,4),
  10:          new OleDbParameter("@NewsTitle", OleDbType.VarChar),
  11:          new OleDbParameter("@NewsContent", OleDbType.VarChar),
  12:          new OleDbParameter("@NewsReporter", OleDbType.VarChar)};
  13:  parameters[0].Value = model.NewsID;
  14:  parameters[1].Value = model.NewsTitle;
  15:  parameters[2].Value = model.NewsContent;
  16:  parameters[3].Value = model.NewsReporter;
  17:  
  18:  return DbHelperOleDb.ExecuteSql(strSql.ToString(), parameters);
<!– .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } –>因为这个代码是使用动软代码生成器自动生成的,所以我可以肯定代码是无误的,可是问题出在我修改了参数,自动生成的代码里包含可以更新ID,我数据 库里的ID是自动增长的,所以不能更新,我也就直接删除了ID,变成上面的错误代码。就这样,问题就出来啦,不管我如何调试,都无济于事,始终不能更新到 数据库。

在琢磨很久之后,换成更加不同的方式来更新数据库,也就是不使用@传参数,直接使用++方式的代码。

++方式的代码:

   1:  string strSql;
   2:  strSql="update news_Table set NewsTitle='"+model.NewsTitle+"',NewsContent='"+model.NewsContent+"',NewsReporter='"+
   3:          model.NewsReporter+"'  where NewsID="+model.NewsID;
   4:  return DbHelperOleDb.ExecuteSql(strSql);

<!– .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } –>使用这种方式可以很顺利的更新数据库里的数据。

这样的结果让我很抓狂。我不知道该怎么办好,我想不通为什么传参数的方式不行,但是直接写的方式就可以。接着就是我疯狂地在Google上搜索的这 方面的资料,但是搜到这方面和我同样的问题的日志不多。但是还是有解决这个问题的解决方案,那就是参数赋值顺序和传入的时候一致,下面贴出正确的代码:

正确传参数的代码:

   1:  StringBuilder strSql = new StringBuilder();
   2:  strSql.Append("update news_Table set ");
   3:  strSql.Append("NewsTitle=@NewsTitle,");
   4:  strSql.Append("NewsContent=@NewsContent,");
   5:  strSql.Append("NewsReporter=@NewsReporter");
   6:  strSql.Append(" where NewsID=@NewsID ");
   7:  
   8:  OleDbParameter[] parameters = {
   9:          new OleDbParameter("@NewsTitle", OleDbType.VarChar),
  10:          new OleDbParameter("@NewsContent", OleDbType.VarChar),
  11:          new OleDbParameter("@NewsReporter", OleDbType.VarChar),
  12:          new OleDbParameter("@NewsID", OleDbType.Integer,4)};
  13:  
  14:  parameters[0].Value = model.NewsTitle;
  15:  parameters[1].Value = model.NewsContent;
  16:  parameters[2].Value = model.NewsReporter;
  17:  parameters[3].Value = model.NewsID;
  18:  
  19:  return DbHelperOleDb.ExecuteSql(strSql.ToString(), parameters);

<!– .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } –>只是参数顺序改变了一下就可以正常更新Access数据库啦,也许这应该是Access的一个Bug吧,也许Access本来就如此,比MSSQL语法要严格。

总结

Access和MSSQL还是有不同的,不能够一味的按照MSSQL的方式去解决Access上的问题,只是有一些相同。

[转载]项目总结-耦合Couple

mikel阅读(958)

[转载]项目总结-耦合Couple – 软件设计开发 – 博客园.

耦合Couple

引言

本文将到的耦合是指的软件开发中的耦合,而且是代码方面的耦合。包括后面讲到的分层,也只是逻辑分层,不是物理分层。

耦合是我们做软件开发经常会遇到的词汇,尤其是在使用面向对象语言进行开发的时候。看到的相关资料也都在说要低耦合,减少耦合。

尽管我们加入了设计模式,分了层,分了模块,做了等等的工作,还是发现存在耦合,还是有人说耦合高了,导致不能修改,修改、维护的代价太大了。直接导致工期不能固定,不能预估,不知道什么时候才能完成任务。

下面就让我们分析一下耦合从何而来?耦合又是什么呢?如何降低耦合呢?耦合能否不再存在呢?耦合可以解除吗?where is the couple?what is the couple?

正文

在我看来,尽管我们分层了,分了模块,应用了设计模式,耦合永远还是存在的。我们能做的是给耦合定义一个限度,做到什么程度就可以了呢?这需要根据项目的context来决定。context包括:时间,资金,人员,需求等一下客观因素。

就像我们分的层一样,分层式为了降低耦合,但是分了层,层之间的耦合是降低了,可是在层的内部的耦合还是需要重新定义的,这里面的耦合不比层之 间的耦合要低。通常我们的项目会分为三层:数据访问层、业务逻辑层、表现层,如果需要的话,我们还会加入服务层。还有在这些层之间交互的数据,实体。因 此,我觉得耦合包括:实体耦合、数据访问层耦合、业务层耦合、服务耦合(如果存在服务层)。

1、实体耦合

什么叫做实体耦合呢?就是实体的公用,尤其存在于从数据库直接生成的实体。例如一个申请实体,需要申请人填写一些信息,例如:姓名、申请标题、 内容。然后由审批的人查看,进行审批,会加入意见,是否同意等信息。同时,申请的时间和处理申请的时间也是关键的信息,也需要在数据库中记录。

生成下面的实体

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.TDD.ConApp
{
public class Application
{
public Guid ApplcaitonID
{
get;
set;
}
public Guid ApplyerID
{
get;
set;

}
public string ApplyTitle
{
get;
set;
}
public string ApplyContent
{
get;
set;
}
public DateTime ApplyDate
{
get;
set;
}
public Guid Checker
{
get;
set;
}
public DateTime CheckDate
{
get;
set;
}
public CheckResult CheckResult
{
get;
set;
}
public string CheckReason
{
get;
set;
}
}
[Flags]
public enum CheckResult
{
Waiting
=1,
Agree
=2,
Disagree
=4
}
}

其实在添加申请的时候,数据库applicaiton表中的有些字段是不用添加的,例如:处理时间,处理人之类和处理相关的字段。那么这个实体 会让做添加功能的程序员很是疑惑,那些字段是必须要赋值的呢?那些不用去管呢?这个类会造成疑惑。就算他通过了解知道了他需要赋值的字段。在后面肯定有一 个申请人和审批人查询申请信息的功能。

有一天需求有了变化,申请人多了一个选项,需要他选择,当然了,内容会加入数据库。因为审批人建立了一些申请的类型,可以对申请信息进行分类,方便统计。

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.TDD.ConApp
{
public class Application
{
public Guid ApplcaitonID
{
get;
set;
}
public Guid ApplyerID
{
get;
set;

}
public string ApplyTitle
{
get;
set;
}
public string ApplyContent
{
get;
set;
}
public DateTime ApplyDate
{
get;
set;
}
public Guid Checker
{
get;
set;
}
public DateTime CheckDate
{
get;
set;
}
public CheckResult CheckResult
{
get;
set;
}
public string CheckReason
{
get;
set;
}
public ApplyType ApplyType
{
get;
set;
}
}
[Flags]
public enum CheckResult
{
Waiting
=1,
Agree
=2,
Disagree
=4
}

[Flags]
public enum ApplyType
{
Family=1,
Social=2,
Personal=4
}
}

数据库要添加一个字段,类要添加一个字段。由于实体是公用的,添加、查询、显示都是用一个实体,但是有的字段有的时候是不用的。这个就给后面的 维护带来很大的困难,可能会发生错误,这个类背负了过多的责任。如果这个字段只是为了添加功能而设立的,显示和查询功能用这个实体的时候就不应该看见它, 要不然他们就需要知道这个字段的意思,是否需要赋值。而且加一个字段,只是为添加功能而设计的字段,需要担心是否显示和查询功能会有问题。就像上面我们的 枚举量是从1开始的,没有0。在显示的时候,如果没有对这个字段赋值,那么这个属性就是枚举量的默认值,0,然后就会报错,不存在0的枚举量值。

这样的类应该被分为多个,添加就是一个添加用的实体,每一个字段都是要赋值的,这样就不会给做添加功能的程序员带来麻烦,减少沟通的成本,加快开发的速度。

实体类应该是专用的,每个实体类都有一个场景,都有自己的context,不应该混用。如果混用,就是实体耦合。这个耦合也是我们应该在最初的时候需要注意的。

2、数据访问层耦合

什么叫做数据访问层耦合呢?先让我们看一个例子。

查询,根据界面上的条件查询申请信息。我们的用户有两类,一个是申请者,一个是审批者。申请者应该查询自己的申请,审批者则可以查询所有的申请。于是有了下面的方法。根据申请时间、申请类型、标题进行查询。

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.TDD.ConApp
{
public class ApplicationDataAccess
{

public List<Application> Find(DateTime? applyDateBegin, DateTime? applyDateEnd, ApplyType applyType, string title)
{
List
<Application> applications = null;

return applications;
}
}
}

可是有一天,上面说需要添加一个条件,就是处理结果,好吧,方法加一个参数吧。后来又说了,加上一个处理时间的参数吧,好吧,加上两个吧。等等,参数列表会越来越长,好像会没有尽头。我们来小小的改造一下,建立一个查询实体,需要查询的字段都放在里面。

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.TDD.ConApp
{
public class ApplicationDataAccess
{

public List<Application> Find(ApplicationFind find)
{
List
<Application> applications = null;

return applications;
}
}
public class ApplicationFind
{
public DateTime? ApplyDateBegin
{
get;
set;
}
public DateTime? ApplyDateEnd
{
get;
set;
}
public ApplyType ApplyType
{
get;
set;
}
public string Title
{
get;
set;
}
public CheckResult CheckResult
{
get;
set;
}
}
}

这下好了,以后如果需要添加查询条件只需要打开find实体,添加一个属性就可以了。当然了,存储过程和一些代码还是需要修改的。可是毕竟参数 不会越来越长了,几个参数看起来也比较舒服。因为在实际的项目中,一个方法,除了这个参数,肯定会有其他的参数,例如方法访问者的信息,用来验证用户合法 性,还可能会包括一些异常信息的反馈等等。

过来几天,问题又来了。申请者需要加一个条件,可是审批者还是原来的条件。好吧,在查询方法中,判断一下用户的类型,然后决定使用的存储过程。 然后处理查询结果的时候,也需要进行判断。而且写好之后,除了测试申请者用户,还需要测试审批者用户,防止审批者使用这个方法出现问题。

代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BeautyCode.TDD.ConApp
{
public class ApplicationDataAccess
{

public List<Application> Find(ApplicationFind find,UserType userType)
{
List
<Application> applications = null;

if (userType == UserType.Applyer)
{
}
else
{
}

return applications;
}
}

public enum UserType
{
Applyer,
Checker
}
public class ApplicationFind
{
public DateTime? ApplyDateBegin
{
get;
set;
}
public DateTime? ApplyDateEnd
{
get;
set;
}
public ApplyType ApplyType
{
get;
set;
}
public string Title
{
get;
set;
}
public CheckResult CheckResult
{
get;
set;
}
}
}

这样每次申请者添加查询条件,都要修改这个方法,还要防止不要破坏审批者使用这个功能。

如果以后加入几种用户类型呢?if。。。else。。。会越写越长,这个方法会越来越难以维护。维护之前要区分那一段是给那一种用户的,修改一 种用户的查询,不要影响了别人。如果存储过程用的是一样,那就更是痛苦了,在存储过程中还需要一堆的if。。。else。。。,那个维护起来就更是麻烦 了。很是痛苦。

这就是数据访问层的耦合。这时候我们应该给每一种用户写一个查询方法,每个用户都有自己的find实体,对应一个自己的存储过程。做到方法和存储过程的专用。下回再来修改一种用户的查询功能的时候,就不用害怕会影响别人了,不用测试其他的用户类型了。

3、业务逻辑层耦合

什么是业务逻辑层耦合呢?

其实和数据访问层耦合差不多,也是由于方法公用产生的。应该用同样的方法来解决,方法专用,不要大家混用一个。

4、服务层耦合

什么时候服务层耦合呢?

也是基于方法的耦合?解决方法同上。

5、面向对象和耦合

封装、继承、多态是面向对象的三个特征。是不是使用面向对象就可以避免耦合呢?答案是:No。

尤其在使用了其中的继承之后,不仅不是消除耦合,反而是引入了耦合。因为,一个对象继承另外一个对象,如果基类修改了,那么继承类也被迫需要进行修改,这不就是引入了耦合,甚至是加重了耦合吗?

继承就代表加重耦合的程度,估计这也是为什么继承不被推荐使用的原因之一吧。

结论

结论就是耦合永远无法消除,我们能做的就是尽量的减少耦合。而且面向对象也不能消除耦合,反而用的不好,还会加重耦合的程度。

【Blog】http://virusswb.cnblogs.com/

【MSN】jorden008@hotmail.com

【说明】转载请标明出处,谢谢

[转载]提搞网站访问速度可做哪些优化

mikel阅读(1023)

[转载]提搞网站访问速度可做哪些优化 – 熊哥 www.relaxlife.net – 博客园.

一、  服务器优化

l  Windows系列:

  • 64位Win2008r2 + Hpyer-V + 负载均衡 + IIS7.5
  • 64位Win2003 + 虚拟机 + 负载均衡 + IIS6
  • 禁止服务器访问外网。关掉不必要的端口。
  • 去掉每个磁盘的所有权限,只保留administrators组和system完全控制权限。
  • IIS7.5标识使用ApplicationPoolIdentity,给目录权限用户是:IIS AppPool\站点名
  • IIS6需要为每个站点建独立的用户,为IIS用户单独设置权限。
  • IIS禁止日志记录。

二、  数据库优化

l  64位MSSQL2008:

  • 少用触发器/游标/外键。多用存贮过程和视图。适量使用索引查询速度会提高很多,每个表的索引尽量不要超过3个。
  • 定时查看死锁的进程和等待进程,并清除,还要及时修正死锁的来源。
  • 复杂的统计报表需要用计划去跑。
  • 查询记录数要使用count(0) 不要使用count(*)。
  • select的时候表字段不要使用*。*查询所有字段是很慢的。
  • 如果字段是索引列可使用UNION ALL 替代 OR,走索引线路查询。
  • 大记录集要使用分页查询。
  • 尽量不要使用TEXT/NTEXT类型,要使用VARCHAR(MAX)/NVARCHAR(MAX)。
  • 尽量不要使用临时表,要使用Table表类型或内嵌视图。

三、  后端优化

l  多使用DbDataReader,尽量不要使用DataTable/DataSet读取数据。

l  for (int i = 0; i < dt.Rows.Count; i++) 应该这么写:

for (int i = 0, len = dt.Rows.Count; i < len; i++)或

foreach (DataRow info in dt.Rows)推荐这种写法

l  少用try catch,用到try catch时要和 finally一起使用。

l  多使用using如:

using (DbDataReader dr = Data.GetDbDataReader(strSQL)) { while (dr.Read()) { }; }

l  大字符串操作请使用StringBuilder 少使用string,字符串比较用Compare,字符串相加用Concat,大字符串相加用StringBuilder Append。

l  变量要在先定义在使用,不要在循环内定义变量。如:

错误的写法是:for (int i = 1; i < 10; i++) { string s = i.ToString(); }

正确写法:string s = string.Empty; for (int i = 1; i < 10; i++) { s = i.ToString(); }

这里还有一个地方要注意的:在字符串相加的时候,如果有int类型的要先转成string类型在相加,减少不必要的装箱拆箱操作。

l  如果你使用的是HTML控件,需要禁用<%@ Page EnableViewState=”false” AutoEventWireup=”false” EnableSessionState=”false”,web.config <pages enableViewState=”false” enableSessionState=”false” />

l  使用foreach替代for

l  操作数据库要使用存贮过程/视图。

l  多使用CACHE对数据缓存。这才是最关键的。NET提供:HttpContext.Current.Cache/HttpRuntime.Cache,共享缓存有Velocity/ memcached

l  可使用<%@ OutputCache Duration=”60″ 缓存页面,可使用Response.BufferOutput = true;/ <%@ Page Buffer=”true” 输出缓冲。

l  可将站点生成静态面页,好处多多。

l  可使用URL重写成伪静态,提供rss/baidu-sitemap/google-sitemap服务,有利于搜索引擎收录。

l  Ajax调用页面要使用.ashx一般处理程序,速度要比.aspx文件要快。Ajax请求要使用POST不要使用GET。

l  发布站点时DLL要Release版本,不要用Debug版本。

l  IIS需要使用集成模式,不要使用经典模式。

l  Web.config 加 <customErrors mode=”On” /> 关闭错误提示。

l  Web.config 加 <compilation Debug=”false” /> 关闭调试模式。

l  使用Server.Transfer替换Response.Redirect

l  多使用泛型集合操作,少用ArrayList。

四、  前端优化

l  最好不使用NET控件,用HTML控件,页面使用CSS+DIV布局。

l  使用JSON + AJAX进行数据交互。

l  要学会使用JQuery提高开发速度。

l  尽量少用<img标记,改用background样式控制背景图片。这样做的好处是方便扩展多皮肤。

l  将CSS background背景图片多张合并成一张图片,保证合并后的图片大小不要超过50K。可将JS文件合并在一个JS文件中,然后使用closure_packer_yui_compiler进行压缩,这样做的好处是可减少客户端连接数。

l  JS代码段不要放在HTML页面,应该放在独立的JS文件里面,好处是JS文件可以CDN/缓存。

l  可将CSS文件放到<head></head>之间,JS文件或代码放到</body>之前,让页面先显示在执行JS。

l  可将网站的图片/CSS/JS/资料/资源放到独立的站点,做下CDN加速,二级域名会有COOKIES,最好使用一级域名。

l  A标记和IMG标记需要加上title/alt,<head>标签内需要加title/keywords/description/rss/sitemap优化,有利于搜索引擎收录。

l  页面可启用gzip压缩。

l  安装FF的插件“YSlow/Page Speed”。

五、  其它优化

[转载]浅谈大型网站动态应用系统架构

mikel阅读(907)

[转载]浅谈大型网站动态应用系统架构_IT新闻_博客园.

动态应用,是相对于网站静态内容而言,是指以c/c++、php、Java、perl、.net等服务器端语言开发的网络应用软件,比如论坛、网络相册、交友、BLOG等常见应用。动态应用系统通常与数据库系统、缓存系统、分布式存储系统等密不可分。

大型动态应用系统平台主要是针对于大流量、高并发网站建立的底层系统架构。大型网站的运行需要一个可靠、安全、可扩展、易维护的应用系统平台做为支撑,以保证网站应用的平稳运行。

大型动态应用系统又可分为几个子系统:

1)Web前端系统

2)负载均衡系统

3)数据库集群系统

4)缓存系统

5)分布式存储系统

6)分布式服务器管理系统

7)代码分发系统

Web前端系统

结构图:

为了达到不同应用的服务器共享、避免单点故障、集中管理、统一配置等目的,不以应用划分服务器,而是将所有服务器做统一使用,每台服务器都可以对多个 应用提供服务,当某些应用访问量升高时,通过增加服务器节点达到整个服务器集群的性能提高,同时使他应用也会受益。该Web前端系统基于 Apache/Lighttpd/Eginx等的虚拟主机平台,提供PHP程序运行环境。服务器对开发人员是透明的,不需要开发人员介入服务器管理

负载均衡系统

负载均衡系统分为硬件和软件两种。硬件负载均衡效率高,但是价格贵,比如F5等。软件负载均衡系统价格较低或者免费,效率较硬件负载均衡系统低,不过对于流量一般或稍大些网站来讲也足够使用,比如lvs, nginx。大多数网站都是硬件、软件负载均衡系统并用。

数据库集群系统

结构图:

由于Web前端采用了负载均衡集群结构提高了服务的有效性和扩展性,因此数据库必须也是高可靠的,才能保证整个服务体系的高可靠性,如何构建一个高可靠的、可以提供大规模并发处理的数据库体系?

我们可以采用如上图所示的方案:

1) 使用 MySQL 数据库,考虑到Web应用的数据库读多写少的特点,我们主要对读数据库做了优化,提供专用的读数据库和写数据库,在应用程序中实现读操作和写操作分别访问不同的数据库。

2) 使用 MySQL Replication 机制实现快速将主库(写库)的数据库复制到从库(读库)。一个主库对应多个从库,主库数据实时同步到从库。

3) 写数据库有多台,每台都可以提供多个应用共同使用,这样可以解决写库的性能瓶颈问题和单点故障问题。

4) 读数据库有多台,通过负载均衡设备实现负载均衡,从而达到读数据库的高性能、高可靠和高可扩展性。

5) 数据库服务器和应用服务器分离。

6) 从数据库使用BigIP做负载均衡。

缓存系统

缓存分为文件缓存、内存缓存、数据库缓存。在大型Web应用中使用最多且效率最高的是内存缓存。最常用的内存缓存工具是Memcached。使用正确的缓存系统可以达到实现以下目标:

1、使用缓存系统可以提高访问效率,提高服务器吞吐能力,改善用户体验。

2、减轻对数据库及存储集服务器的访问压力。

3、Memcached服务器有多台,避免单点故障,提供高可靠性和可扩展性,提高性能。

分布式存储系统

结构图:

Web系统平台中的存储需求有下面两个特点:

1) 存储量很大,经常会达到单台服务器无法提供的规模,比如相册、视频等应用。因此需要专业的大规模存储系统。

2) 负载均衡cluster中的每个节点都有可能访问任何一个数据对象,每个节点对数据的处理也能被其他节点共享,因此这些节点要操作的数据从逻辑上看只能是一个整体,不是各自独立的数据资源。

因此高性能的分布式存储系统对于大型网站应用来说是非常重要的一环。(这个地方需要加入对某个分布式存储系统的简单介绍。)

分布式服务器管理系统

结构图:

随着网站访问流量的不断增加,大多的网络服务都是以负载均衡集群的方式对外提供服务,随之集群规模的扩大,原来基于单机的服务器管理模式已经不能够满足我们的需求,新的需求必须能够集中式的、分组的、批量的、自动化的对服务器进行管理,能够批量化的执行计划任务。

在分布式服务器管理系统软件中有一些比较优秀的软件,其中比较理想的一个是Cfengine。它可以对服务器进行分组,不同的分组可以分别定制 系统配置文件、计划任务等配置。它是基于C/S 结构的,所有的服务器配置和管理脚本程序都保存在Cfengine Server上,而被管理的服务器运行着 Cfengine Client 程序,Cfengine Client通过SSL加密的连接定期的向服务器端发送请求以获取最新的配置文件和管理命令、脚本程序、补丁安装等任务。

有了Cfengine这种集中式的服务器管理工具,我们就可以高效的实现大规模的服务器集群管理,被管理服务器和 Cfengine Server 可以分布在任何位置,只要网络可以连通就能实现快速自动化的管理。

代码发布系统

结构图:

随着网站访问流量的不断增加,大多的网络服务都是以负载均衡集群的方式对外提供服务,随之集群规模的扩大,为了满足集群环境下程序代码的批量分发和更新,我们还需要一个程序代码发布系统。

这个发布系统可以帮我们实现下面的目标:

1) 生产环境的服务器以虚拟主机方式提供服务,不需要开发人员介入维护和直接操作,提供发布系统可以实现不需要登陆服务器就能把程序分发到目标服务器。

2) 我们要实现内部开发、内部测试、生产环境测试、生产环境发布的4个开发阶段的管理,发布系统可以介入各个阶段的代码发布。

3) 我们需要实现源代码管理和版本控制,SVN可以实现该需求。

这里面可以使用常用的工具Rsync,通过开发相应的脚本工具实现服务器集群间代码同步分发。