[转载]SQL性能调优实践——SELECT COUNT

mikel阅读(1090)

[转载]SQL性能调优实践——SELECT COUNT – 潇湘隐者 – 博客园.

最近想深入学习SQL,在网上搜索到一些SQL 优化的资料要么是张冠李戴,Oracle 优化的资料硬是弄成啦MS SQL 优化的资料,而且被很多人转 载,收藏,有些要么有些含糊不清,好像是那么回事,也没经过验证,实践出真知!下面是我对SELECT COUNT(*) SELECT COUNT(1)SELECT COUNT (0), SELECT COUNT(Field)等孰优孰劣 的测试结果,如果测试方法有什么不足,也希望大家给点建议。

首先我们来看看测试的机器、以及开发环境吧:双核处理器 T6670  2G DDR2的内存 数据版本如下图所示:

然后建一个简单的测试表

代码

CREATE TABLE Employee
(
[EmployeeID] INT IDENTITY(1,1), 雇员ID
[EmployeeName] NVARCHAR(20), 雇员姓名
[SEX] BIT, 性别
[Department] NVARCHAR(20), 部门
CONSTRAINT [PK_Employee_ID_Name] PRIMARY KEY (EmployeeID, EmployeeName)
)

插入一百 万数据

DECLARE @Index INT;
SET @Index = 1;
WHILE @Index < 1000000
BEGIN
INSERT INTO Employee
VALUES(Employee + STR(@Index), 0, 技术部门);

SET @Index = @Index + 1;
END

建立非聚集索引
CREATE INDEX IDX_Employee_Department ON Employee([Department]);

好,到目前为止我们已经把测试用的表、数据都弄好啦,接下来我们来看看执行一次SELECT COUNT 的使用时间

DBCC DROPCLEANBUFFERS;

DBCC FREEPROCCACHE;

SET STATISTICS TIME ON;

SELECT COUNT(0) FROM Employee

SET STATISTICS TIME OFF;

我们会得到下面的输出结果

DBCC 执 行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

DBCC 执 行完毕。如果DBCC 输出了错误信息,请与系统管理员联系。

SQL Server 执行时间:

CPU 时间= 219 毫秒,占用时间= 1033 毫秒。

接下来我们来看看各种Count的实际执行计划,截图如下

我很纳闷为什么执行计划都是一样的,希望有高手能解答。

接下来,那么我们把上面的脚本执行10,把每次得到的数据记录下来,然后我们依次用 1 SELECTCOUNT()FROM Employee、 SELECTCOUNT(*)FROM Employee

等替换SELECT COUNT(0) FROM Employee 脚本,如下所示

DBCC DROPCLEANBUFFERS;

DBCC FREEPROCCACHE;

SET STATISTICS TIME ON;

SELECT COUNT(1) FROM Employee

SET STATISTICS TIME OFF;

依葫芦画瓢每段脚本执行10次,最后我们求得 到的结果的平均值,为了形象显示,我用Excel把数据显示如下:

SELECT COUNT(1) FROM Employee

SELECT COUNT(0) FROM Employee

SELECT COUNT(*) FROM Employee

SELECT COUNT(EmployeeName) FROM Employee

从实验结果来看,执行快慢的顺序为: COUNT(EmployeeName) > COUNT(0) >= COUNT(1) > COUNT(*);从实验结果来看,我们至少验证了 COUNT(0) >= COUNT(1) > COUNT(*)的结论, 网上有篇帖子《SQL Server 索引结构及其使用》篇所下的结论count(*)不比count(字段)慢 显然是不严谨的,他只做了一次实验, 而我们这里是10次结果的平均值。那么现在问题来了,为什么COUNT(EmployeeName)要快于COUNT(0) >= COUNT(1),它如果不是主键、字段没有索引呢?网上 不是有些资料显示COUNT(1)效率最高,速度最快吗? 我们10次得到平结值有没有误差呢?抽样能否反映事实呢?下面我用这个方法来大量获得语句执行时间,然后求平均值,(我觉得这方法应该是可以反映实际 CPU时间的)如果有不妥的地方,也希望大家指正。 创建下面一个表

CREATE TABLE ExcuteTime
(
[Type] VARCHAR(10), 不同COUNT类型
[CpuTime] FLOAT 语句执行的毫秒
)

代码

得到COUNT(1)100次的执行时间
DECLARE @BeginTime DATETIME;
DECLARE @Num INT;
SET @Num = 1;
WHILE @Num <= 100
BEGIN
SET @BeginTime = GETDATE();
SELECT COUNT(1) FROM Employee;
INSERT INTO ExcuteTime
VALUES(Count(1), DATEDIFF(ms, @BeginTime,GETDATE()));
SET @Num = @Num + 1;
END
GO

然后也 依次得到其它几种SQl 的执行时间,另外我们也把COUNT(Department)得数据加入进来,下面是我得到的实验结果的平均值

COUNT(1) COUNT(0) COUNT(*) COUNT(EmployeeName) COUNT(Department)

100.09 99.27 100.28 65.95 134.13

数据显示也与上面的测试结果相一致,虽然得到了这些结果,不过深层 次的原因我还是没明白,比如COUNT(0) 比 COUNT(1) 稍稍快些(可以忽略,如果是统计偏差)希望在以后的深入学习中能够参透,也希望数据库方面的高手给与解答和讨论。

[转载]MongoDB及其C#驱动

mikel阅读(1035)

[转载]MongoDB及其C#驱动 – 李占卫的网上家园 – 博客园.

1:简单介绍

MongoDB是一款分布式文档数据库,支持类似关型数据库的主从结构,文档以二进制Json形式存储,无锁,无事 务,有索引。

2:安装步骤

第一步:下载程序集http://www.mongodb.org/downloads

第 二部:解压并抽取相关的bin目录到C:\Mongo下(这个任意选择) ,并在该目录下建db文件夹。

第三部:打开CMD窗口,进入 到C:\Mongo\bin目录下,运行mongod.exe –dbpath=”c:\Mongo\db”

第四步:再开CMD窗口 (保持以前的窗口不关闭)运行mongo.exe。在这个窗口你可以执行简单的命令,以下简单列出几个

> j = { name : “mongo” };
{“name” : “mongo”}
> t = { x : 3 };
{ “x” : 3  }
> db.things.save(j);
> db.things.save(t);
> db.things.find();
{ “_id” : ObjectId(“4c2209f9f3924d31102bd84a”), “name” : “mongo” }
{ “_id” : ObjectId(“4c2209fef3924d31102bd84b”), “x” : 3 }
>
> for (var i = 1; i <= 20; i++) db.things.save({x : 4, j : i});
> db.things.find();
> var cursor = db.things.find();
> while (cursor.hasNext()) printjson(cursor.next());
> db.things.find().forEach(printjson);
> var cursor = db.things.find();
> printjson(cursor[4]);
> var arr = db.things.find().toArray();
> arr[5];
> db.things.find({name:”mongo”}).forEach(printjson);
> db.things.find({x:4}, {j:true}).forEach(printjson);
> printjson(db.things.findOne({name:”mongo”}));
> db.things.find().limit(3);

3:C# 版的驱动

以下列出几款C#版的驱动,并作简单介绍。

第一款:mongodb-csharp 项目地址:http://github.com/samus/mongodb-csharp

简 单介绍:该驱动是比较全的驱动,更新随度非常快,有人已经应用到项目中去,并且表现非常好。目前他们的团队正在致力于连接管理这部分功能的扩展例如:自动 重连,连接池等等。

第二款:mongodb-net 项目地址:http://code.google.com/p/mongodb-net/

简 单介绍:该开发还在进行中,有些功能还没有实现。

第三款:MongoDB.Emitter 项目地址:http://bitbucket.org/gwbasic/mongodb.emitter/

简 单介绍:提供了强类型支持

第四款:CSMongo 项目地址:http://somewebguy.wordpress.com/2010/02/21/building-a-mongo-driver-part-1/

简 单介绍:实现了部分功能,并且没有可下载的代码。但是你可以看他的博客以了解这种驱动的思想。

第五款:simple- mongodb 项目地址:http://code.google.com/p/simple-mongodb/

简 单介绍:没有提供源代码,以JSon为核心。

最后一个是对第一款某些地方的增强,地址http://deserialized.com/convert-csharp-classes-to-and-from-mongodb-documents-automatically-using-net-reflection

第 六款:NoRM 项目地址:http://github.com/atheken/NoRM

介 绍:增强了第一种,支持强类型。

排名部分先后,我现在再看的是 NoRM 和mongodb-csharp。

备 注:

虽然这些资料来自于网路,但是为了方便大家学习。我还是厚着脸皮放到首页,见谅啊。

[转载]记一复杂页面的前端优化(2) - 其他优化

mikel阅读(975)

[转载]记一复杂页面的前端优化(2) – 其他优化 – BearRui(AK-47) – 博客园.

上一篇 “记 一复杂页面的前端优化(1) – 不一样的延迟加载“, 说了 下对弹出窗口的优化,接下来说说其他的优化,先把界面图贴出来,方便对照:

下拉列表优化

然后创建自己的输入框和弹出框。当页面只有1,2个select的时候,没发现有什么问题,但当页面出现7,8个select的时候,熏染速度 明显慢了很多,可以看着1个个select变成input,主要原因还是js执行的太多了(动态生成html,绑定事件)等等。这个是影响页面渲染速度的 1个大问题,所以第一个要解决的就是这个select。因原始的select外观是在是不行,而且又不能修改样式,所以我们选择了1个JQueryselectbox 插件,该插件的实现原理:在页面加载完毕后,隐藏原始的select,然后创建自己的输入框和弹出框。当页面只有1,2个select的时候,没 发现有什么问题,但当页面出现7,8个select的时候,熏染速度明显慢了很多,可以看着1个个select变成input.主要原因还是js执行的太 多了(动态生成html,绑定事件)等等。这个是影响页面渲染速度的1个大问题,所以第一个要解决的就是这个select。

目前的解决方案是服务器端和js结合使用,通过jsp的标签(net应该叫自定义控件)生成html代码,并不生成任何js来绑定事件,而且当 用户第一次点击input的时候,才绑定所有事件,弹出下拉窗口。这样就完全解决了渲染的问题,因为不需要js来生成html,也不需要页面加载的时候去 绑定所有事件。

右下的数据列表延迟加载

右下方的数据列表,默认只显示基本信息,当用户点击的时候才展开详细信息,一般用户只有在编辑和删除的时候才会用到详细信息,大部分情况可能不 会用编辑和删除,也就不需要展开详细信息。之前的做法就是在加载列表的时候就把详细信息的html都生成好,只是隐藏一下,所以加载列表就比较慢。把详细 信息改成延迟加载,当用户单击某行数据,才去生成对应的详细信息html代码,并展开显示。

png图片转gif

这个页面用了很多PNG图片,比如收入、支出的图标,左边分类的图片,选择框的图标。而png图片在ie6下要做单独处理,为了提高性能,跟设 计师商量后,把一些图片转成gif的。虽然gif的转了后效果没PNG的好看,但还可以接受,而且也是透明的,于是就通过CSS HACK,让在IE6下使用gif图片,在其他浏览器下使用png图片。这样就可以提高IE6下的速度,而又不用降低其他浏览器的界面效果。

延迟执行ajax

左边每个分类前面有个选择框,当用户选择(或去掉选择)某个分类的时候,都会引发ajax刷新右边的数据列表。这里就可能出现这种情况,比如用户想选择3 个分类查看,需要点击3次选择,之前的做法,每次点击都会触发一次ajax,这样就触发了3个ajax了,其实对于用户来说,只有最后一次ajax是有用 的,前面2次不但浪费资源,而且影响性能。当然你可能会想到我们可以abort前面的ajax请求,但要注意abort只是abort客户端的执行,服务 器端还是会接收到请求并执行完毕。于是对这个ajax做了个延迟,每次点击后延迟0.5秒执行,如果用户在0.5秒内再一次点击,则取消之前的ajax。 这样就可以避免一些不必要的ajax请求了。

后语

今天这篇文章只是介绍优化的方法,并没写任何代码,个人觉的这种方法不需要写代码,大家一看应该就知道。

该页面中经过这些优化后,页面总的加载速度(包括资源下载、解析、执行、页面呈现)提高了3倍左右。

[转载]命令模式-2

mikel阅读(964)

[转载]命令模式-2 – 云飞龙行 – 博客园.

3  模式讲解

3.1  认识命令模式

(1)命令模式的关键
命令模式的关键之处就是把请求封装成为对象,也就是命令对象,并定义了统一的执行操作的接口,这个命令对象可以被存储、转发、记录、处理、撤销等,整个命 令模式都是围绕这个对象在进行。
(2)命令模式的组装和调用
在命令模式中经常会有一个命令的组装者,用它来维护命令的“虚”实现和真实实现之间的关系。如果是超级智能的命令,也就是说命令对象自己完全实现好了,不 需要接收者,那就是命令模式的退化,不需要接收者,自然也不需要组装者了。
而真正的用户就是具体化请求的内容,然后提交请求进行触发就好了。真正 的用户会通过invoker来触发命令。
在实际开发过程中,Client和Invoker可以融合在一起,由客户在使用命令模式的时候,先进行命令对象和接收者的组装,组装完成后,就可以调用命 令执行请求。
(3)命令模式的接收者
接收者可以是任意的类,对它没有什么特殊要求,这个对象知道如何真正执行命令的操作,执行时是从command的实现类里面转调过来。
一 个接收者对象可以处理多个命令,接收者和命令之间没有约定的对应关系。接收者提供的方法个数、名称、功能和命令中的可以不一样,只要能够通过调用接收者的 方法来实现命令对应的功能就可以了。
(4)智能命令
在标准的命令模式里面,命令的实现类是没有真正实现命令要求的功能的,真正执行命令的功能的是接收者。
如果命令的实现对象比较智能,它自己就能真实地实现命令要求的功能,而不再需要调用接收者,那么这种情况就称为智能命令。
也可以有半智能的命令,命令对象知道部分实现,其它的还是需要调用接收者来完成,也就是说命令的功能由命令对象和接收者共同来完成。
(5) 发起请求的对象和真正实现的对象是解耦的
请求究竟由谁处理,如何处理,发起请求的对象是不知道的,也就是发起请求的对象和真正实现的对象是解耦的。发起请求的对象只管发出命令,其它的就不管了。
(6) 命令模式的调用顺序示意图
使用命令模式的过程分成两个阶段,一个阶段是组装命令对象和接收者对象的过程,另外一个阶段是触发调用Invoker,来让命令真正执行的过程。
先看看组装过程的调用顺序示意图,如图4所示:

图4  命令模式组装过程的调用顺序示意图
接下来再看看真正执行命令时的调用顺序示意图,如图5所示:

图5  命令模式执行过程的调用顺序示意图

3.2  参数化配置

所谓命令模式的参数化配置,指的是:可以用不同的命令对象,去参数化配置客户的请求。
像前面描述的那样:客户按下一个按钮,到底是开机还是重启,那要看参数化配置的是哪一个具体的按钮对象,如果参数化的是开机的命令对象,那就执行开机的功 能,如果参数化的是重启的命令对象,那就执行重启的功能。虽然按下的是同一个按钮,相当于是同一个请求,但是为请求配置不同的按钮对象,那就会执行不同的 功能。
把这个功能用代码实现出来,一起来体会一下命令模式的参数化配置。
(1)同样先定义主板接口吧,现在想要添加一个重启的按钮,因此主板需要添加一个方法来实现重启的功能,示例代码如下:

01 /**
02 * 主板的接口
03 */
04 public interface MainBoardApi {
05 /**
06 * 主板具有能开机的功能
07 */
08 public void open();
09 /**
10 * 主板具有实现重启的功能
11 */
12 public void reset();
13 }

接口发生了改变,实现类也得有相应的改变,由于两个主板的实现示意差不多,因此还是只示例一个,示例代码如下:

01 /**
02 * 技嘉主板类,命令的真正实现者,在Command模式中充当Receiver
03 */
04 public class GigaMainBoard implements MainBoardApi{
05 /**
06 * 真正的开机命令的实现
07 */
08 public void open(){
09 System.out.println("技嘉主板现在正在开机,请 等候");
10 System.out.println("接通电源......");
11 System.out.println("设备检查......");
12 System.out.println("装载系统......");
13 System.out.println("机器正常运转起 来......");
14 System.out.println("机器已经正常打开,请操 作");
15 }
16 /**
17 * 真正的重新启动机器命令的实现
18 */
19 public void reset(){
20 System.out.println("技嘉主板现在正在重新启动 机器,请等候");
21 System.out.println("机器已经正常打开,请操 作");
22 }
23 }

(2)该来定义命令和按钮了,命令接口没有任何变化,原有的开机命令的实现也没有任何变化,只是新添加了一个重启命令的实现,示例代码如下:

01 /**
02 * 重启机器命令的实现,实现Command接口,
03 * 持有重启机器命令的真正实现,通过调用接收者的方法来实现命令
04 */
05 public class ResetCommand implements Command{
06 /**
07 * 持有真正实现命令的接收者——主板对象
08 */
09 private MainBoardApi mainBoard = null;
10 /**
11 * 构造方法,传入主板对象
12 * @param mainBoard 主板对象
13 */
14 public ResetCommand(MainBoardApi mainBoard) {
15 this.mainBoard = mainBoard;
16 }
17
18 public void execute() {
19 //对于命令对象,根本不知道如何重启机器,会转调主板对象
20 //让主板去完成重启机器的功能
21 this.mainBoard.reset();
22 }
23 }

(3)持有命令的机箱也需要修改,现在不只一个命令按钮了,有两个了,所以需要在机箱类里面新添加重启的按钮,为了简单,没有做成集合。示例代码如 下:

01 /**
02 * 机箱对象,本身有按钮,持有按钮对应的命令对象
03 */
04 public class Box {
05 private Command openCommand;
06 public void setOpenCommand(Command command){
07 this.openCommand = command;
08 }
09 public void openButtonPressed(){
10 //按下按钮,执行命令
11 openCommand.execute();
12 }
13 /**
14 * 重启机器命令对象
15 */
16 private Command resetCommand;
17 /**
18 * 设置重启机器命令对象
19 * @param command
20 */
21 public void setResetCommand(Command command){
22 this.resetCommand = command;
23 }
24 /**
25 * 提供给客户使用,接收并相应用户请求,相当于重启按钮被按下触发的方法
26 */
27 public void resetButtonPressed(){
28 //按下按钮,执行命令
29 resetCommand.execute();
30 }
31 }
32
33 4)看看客户如何使用这两个按钮,示例代码如下
01 public class Client {
02 public static void main(String[] args) {
03 //1:把命令和真正的实现组 合起来,相当于在组装机器,
04 //把机箱上按钮的连接线插接到主板上。
05 MainBoardApi mainBoard = new GigaMainBoard();
06 //创建开机命令
07 OpenCommand openCommand = new OpenCommand(mainBoard);
08 //创建重启机器的命令
09 ResetCommand resetCommand = new ResetCommand(mainBoard);
10 //2:为机箱上的按钮设置对应的命令,让按钮知道该干什么
11 Box box = new Box();
12 //先正确配置,就是开机按钮对开机命令,重启按钮对重启命令
13 box.setOpenCommand(openCommand);
14 box.setResetCommand(resetCommand);
15
16 //3:然后模拟按下机箱上的按钮
17 System.out.println("正确配置下 ------------------------->");
18 System.out.println(">>> 按下开机按钮:>>>");
19 box.openButtonPressed();
20 System.out.println(">>> 按下重启按钮:>>>");
21 box.resetButtonPressed();
22
23 //然后来错误配置一回,反正是进行参数化配置
24 //就是开机按钮对重启命令,重启按钮对开机命令
25 box.setOpenCommand(resetCommand);
26 box.setResetCommand(openCommand);
27 //4:然后还是来模拟按下机箱上的按钮
28 System.out.println("错误配置下 ------------------------->");
29 System.out.println(">>> 按下开机按钮:>>>");
30 box.openButtonPressed();
31 System.out.println(">>> 按下重启按钮:>>>");
32 box.resetButtonPressed();
33 }
34 }

运行一下看看,很有意思,结果如下:

[转载]在 Asp.net MVC 2 中解决页面提交数据并发问题

mikel阅读(926)

[转载]在 Asp.net MVC 2 中解决页面提交数据并发问题 – Dreampuf – 博客园.

通过本篇,你能了解到 ASP.NET MVC 模型绑定处理过程,一种解决并发颗粒度到一条数据的方法.

* 如何解决互联网中某条数据的并发问题
在一个页面提交数据库前把从数据库取出的数据和提交时数据库中的数据比较,不同则给出提示.
在和其他童鞋的讨论中,都明确的指出了采用HashCode的方案,HashCode作为区分对象不同的一种方案,确实可行,但也有 反对的声音.采用HashCode难免会涉及到对于实体对象的HashCode生成的问题,而实体对象的Hash又是根据自身属性生成,而属性又 包含了其他关系,这种连带作用十分可观,也想过采用对于所有值属性采用Hash生成,对于关系对象着获取ID后再生成,但表数量也不少,如果考虑反射则每 次提交都要遍历所有属性,性能客观,这还仅仅只是一次修改呢,折腾再三有了这样的方法.
在每张表中加入一个新的DateTime字段,每次修改赋最新值.
对,就是这么简单,每次读取时获得DateTime对象,返回到客户端页面,提交时再和数据库中对象比较.DateTime对象精确到毫秒,这种程度已经 足够应付需求中所定义的并发.


通过Ticks构造的DateTime对象

*如何在ASP.NET MVC中使用DateTime对象作为验证数据是否已过期依据

ASP.NET MVC 的模型肩负着承载数据的责任.同时又很容易配置,使用.下面这张图介绍了ASP.NET MVC如何处理实体对象模型的(截自Pro Asp.net MVC 2 Framework.

模型作为.net对象传递给视图后经过渲染后,呈现在HTML中.这就是回应客户端响应的流程.

同样的,作为客户端,以单纯的字符串是无法直接在.net中使用的,又需要进行模型绑定.才能转换为.net对象,继而使用他.

知道了整个模型的整个流程后,我们有了几个对策,思考再三决定传递DateTime对象的Ticks作为依据,因为直接输出DateTime最多也就精确 到秒,不能和数据库中的原始数据DateTime匹配.所以获得其Ticks,作为字符串传输,然后返回的时候再解析.有了解决思路后就好做了.

这是我们的模型,注意其中的HashTime可空属性. 注意他标上了一个UIHint Attribute,能够绑定指定的用户控件,继而单独的渲染该属性.

代码

1 public partial class ModelSimpleChangeUser : IHashTime
2 {
3 [HiddenInput]
4 [DisplayName(用户编号)]
5 [Required]
6 public int UserId { get; set; }
7
8 [Required(ErrorMessage=名称必须不为空)]
9 [DisplayName(用户名)]
10 [DataType(System.ComponentModel.DataAnnotations.DataType.Text)]
11 public string UserName { get; set; }
12 [Required(ErrorMessage=Email必须存在)]
13 [DisplayName(电子邮件)]
14 [DataType(System.ComponentModel.DataAnnotations.DataType.EmailAddress)]
15 public string Email { get; set; }
16
17 [HiddenInput(DisplayValue = false)]
18 [UIHint(HiddenDateTime)]
19 [Required(ErrorMessage=HashTime必须存在)]
20 public DateTime? HashTime { get; set; }
21
22
23
24 public ModelSimpleChangeUser() { }
25 public ModelSimpleChangeUser(ES.DAL.User u)
26 {
27 this.UserId = u.UserId;
28 this.Email = u.Email;
29 this.HashTime = u.HashTime;
30 this.UserName = u.UserName;
31 }
32
33 public ES.DAL.User ChangeTo(ES.DAL.User user)
34 {
35 if(UserId == user.UserId && user.HashTime.HasValue)
36 {
37 user.UserName = UserName;
38 user.Email = Email;
39 }
40 return user;
41 }
42 }

整个编辑视图也就一行起作用的代码.之所以能够这样是因为ASP.NET MVC 2强大的模型特性标记功能,他能够在模型中声明如何显示,这就使得视图和模型完全分离,在视图里我都没有内联ViewPage的泛型类.使得页面可以编写 完全独立的效果,而不必纠缠于和模型属性配对的问题中.

<%= Html.EditorForModel() %>

在HiddenDateTime控件实现了一个简单的从当前实体模型对象读取Ticks的功能,由于是可空DateTime,所以处理了为null情况 (标记字段在测试的时候有些是Null,所以这里给处理了一下,如果在真实的环境中这是不存在的,因为在建立和修改时都已经自动赋值了)

代码

1 <%@ Control Language=C# Inherits=System.Web.Mvc.ViewUserControl<DateTime?> %>
2
3 <%
4 Dictionary<string, object> others = new Dictionary<string, object>();
5 //others.Add(“Oth”, “ccc”);
6 %>
7 <%= Html.Hidden(“”, Model.HasValue ? Model.Value.Ticks : 1, others)%>

效果如下:

然后是绑定从客户端发来的数据问题.在这个点上费了很久,后来还是在书上找到了解决办法.
首先要知道Asp.net MVC是从那些地方以怎样的顺序获取了数据,下面是获取的顺序.

1. Request.Form
2. RouteData.Values
3. Request.QueryString
4. Request.Files
5. null

前台输出的是DateTime的Ticks而不是默认的DateTime输出,所以还需要自己解析下.这里有一段 ValueProviderFactroy的代码是截自书中的,在Global.asax的Appcation_Start事件注册后,就能截获所有参数 的传递了.

1 protected void Application_Start()
2 {
3 AreaRegistration.RegisterAllAreas();
4
5 RegisterRoutes(RouteTable.Routes);
6
7 ValueProviderFactories.Factories.Insert(0, new ES.WEB.Models.HiddentTimeValueProviderFactory());
8 }

下面是那个解析工厂

代码

public class HiddentTimeValueProviderFactory : ValueProviderFactory
{
public override IValueProvider GetValueProvider(ControllerContext ctx)
{
return new HiddentTimeValueProvider(ctx.HttpContext.Request);
}

private class HiddentTimeValueProvider : IValueProvider
{
private HttpRequestBase request;
public HiddentTimeValueProvider(HttpRequestBase Request)
{
request
= Request;
}

public bool ContainsPrefix(string prefix)
{
return HashTime.Equals(prefix, StringComparison.OrdinalIgnoreCase);
}

public ValueProviderResult GetValue(string key)
{
var result
= ContainsPrefix(key)
? new ValueProviderResult(new DateTime(long.Parse(request[HashTime])), null, CultureInfo.CurrentCulture)
:
null;
return result;
}
}
}

自此我们的代码已经完全可以跑起来了,在控制器里对传过来的数据直接分析,看Ticks是否相等就能判断有并发情况了.当然在返回错误的视图中我们也做了 一些改善用户体验的处理.

下面是我们的控制器:

1 public ActionResult Edit(Models.ModelSimpleChangeUser users)
2 {
3 return DoSafe(() =>
4 {
5 var u = UserManager.FindById(users.UserId);
6 if (u.HashTime != null)
7 if(users.HashTime != null &&
8 users.HashTime.Value.Ticks == u.HashTime.Value.Ticks)
9 {
10 u.UserName = users.UserName;
11 u.Email = users.Email;
12 UserManager.Update(u);
13 return RedirectToAction(Index) as ActionResult;
14 }
15 ViewData[lastAction] = Edit;
16 ViewData[lastController] = User;
17 return View(RetiredPage, users);
18 });
19 }

注意15,16行,他将当前处理的Controller和Action的名称都保存了起来,以备在视图中使用,同样将用户输入的数据返回了出去.

下面是视图页面:

1 <asp:Content ID=RetiredMain ContentPlaceHolderID=MainContent runat=server>
2
3 <h2>对不起,您访问的页面已过期!</h2>
4 <p>
5 您所操作的原始数据可能被更改,请您返回获取最新数据.
6 </p>
7 <%= Html.ActionLink(返回,
8 ViewData[BaseController.LastAction].ToString(),
9 ViewData[BaseController.LastController].ToString(),
10 Model, new { id = PostBackLink}) %>
11 <script type=text/JavaScript>
12 setTimeout(function () {
13 var g = document.getElementById(PostBackLink);
14 try { g.click(); } catch (ex) { }
15 }, 2000);
16 </script>
17 </asp:Content>

用HtmlHelper生成了一个超链接,使用了我们之前保存的控制器数据,从而得知是在哪里引发了错误,并且还保存了用户输入的数据模型,最后给这个超 链接赋值了一个id属性,供我们在客户端脚本上使用.

在客户端脚本里我们触发了一个延迟脚本,两秒后触发返回超链接的click事件,由于在FireFox下没有该对象,所以只是简单的容错了一下,还有更多 FireFox下的超链接的脚本触发请移步JavaScript模 拟用户单击事件.

整个过程还是相当简单的,不过条条大路通罗马,不一定都要坐技术含量高的飞机嘛.
前些天分 享了一个封装了EntityFramework的操作的类,这里又拓展了他,将HashTime自动写入,这里就没考虑泛型,因为数据的读写实在 太频繁了,就将所有实体继承一个IHashTime接口,接口只有一个HashTime属性,然后封装了一个方法,在每次数据创建或者改写的时候调用.从 而整个并发问题迎刃而解.

1 private void setHashTime(object o)
2 {
3 (o as IHashTime).HashTime = DateTime.Now;
4 }

[转载]DedeCMS:tag_list使用教程

mikel阅读(891)

[转载]tag_list [DedeCMS维基].

list|列表标签

  • 标签名称:list
  • 功能说明:表示列表模板里的分页内容列表
  • 适用范围:仅列表模板 list_*.htm
  • 基本语法

{dede:list col= titlelen= infolen= imgwidth= imgheight= listtype= orderby= pagesize= orderway=}{/dede:list} * 相关函数: 文件\include\arc.listview.class.php第504行
获得一个单列的文档列表
function GetArcList($limitstart=0,$row=10,$col=1,$titlelen=30,$infolen=250,
$imgwidth=120,$imgheight=90,$listtype=“all”,$orderby=“default”,$innertext=””,$tablewidth=“100”,$ismake=1,$orderWay='desc') * 参数说明: col=
内容列数
titlelen= 标题长度
infolen=
内容摘要长度
imgwidth= 缩略图宽
imgheight=
缩略图高
orderby=” 排序方式,有效的排序方式有 senddate、pubdate、id、click、lastpost、postnum ,默认为 sortrank
pagesize=’20’ 分页大小

  • 底层模板字段

ID(同 id),typeid,sortrank,flag,ismake,channel,arcrank,click,money,title,shorttitle,color,writer, source,litpic(同picname),pubdate,senddate,mid, lastpost,scores,goodpost,badpost,notpost, description(同infos),filename, image, imglink, fulltitle, textlink, typelink,plusurl, memberurl, templeturl, stime(pubdate 的”0000-00-00”格式)
注:list标记除了支持档案表的基本模板变量外,还支持附加表的字段,你可以在模型管理中知道附加表支持列表使用的字段有哪些。

  • 使用实例

这个标签是系统常用标签,通常用于显示列表内容,我们可以在系统的list_*.htm这类模板文件中看到这个标签,我们以 list_article.htm为例:
{dede:list pagesize=’10’}
<li>
[field:array runphp=’yes’]@me = (empty(@me[‘litpic’]) ? ”” : ”<a href='{@me[‘arcurl’]}’ class=’preview’><img src='{@me[‘litpic’]}’/></a>”); [/field:array]
<a href=”[field:arcurl/]” class=“title”>[field:title/]</a>
<span class=“info”>
<small>日期:</small>[field:pubdate function=“GetDateTimeMK(@me)”/]
<small>点击:</small>[field:click/]
<small>好评:</small>[field:scores/]
</span>
<p class=“intro”>
[field:description/]…
</p>
</li>
{/dede:list}
我们查看默认页面显示效果:

这个标签是同pagelist标签配合使用的,用于显示完整的列表分页效果。
列表标签中如果需要显示附加字段,需要在系统后台[核心]-[内容模型管理],编辑一个内容模型(这里以软件为例),在“列表附加字段”加入相应字段即 可。

如果需要修改列表显示的核心代码,可以查看\include\arc.listview.class.php的相关内容。

[转载]简简单单——无限级数据JSON格式及JS解析

mikel阅读(1056)

[转载]简简单单——无限级数据JSON格式及JS解析 – 壊小子 – 博客园.

公司要做个Flash效果的页面,需要个无限级树,显示用户邀请的好友及其下线,由此就构成了无限级的关系,可能下线有无限多。Flash需要 JSON格式的数据,于是,就有了如下代码:(PHP实现,C#类似,JSON库自己去下吧~~)

代码

以上代码返回一个JSON字符串,如:

{‘0’:{‘fuid’:’950′,’funame’:’郭梓原’,’furl’:’/home /space.php?uid=950′,’fchilds’:{‘invitezcount’:0}},’invitezcount’:1,’invitecount’:1}

这里只查询了一个

接下来用JS进行以下解析:

代码

<script type=text/JavaScript src=http://ajax.googleapis.com/ajax/libs/JQuery/1/JQuery.min.js></script>
<script type=text/JavaScript>
//fuid 用户ID
//
funame 用户名称
//
furl 用户主页地址
//
invitezcount 每 个用户的子类数量
//
invitecount 该用户的所有下级数量
var str = “”;
function GetShow(obj,qi,index){

for (var one in obj)
{
for(var key in obj[one])
{
index
++;
if(key==fchilds&&obj[one][key][invitezcount]!=0){
var aqi = qi;
if(index>1)aqi+=&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

GetShow(obj[one][key],aqi,index)
}
if(key != fchilds){
if(key==fuid){
str
+= qi+key+=+obj[one][key] + ,;
}
else{
str
+= key+=+obj[one][key] + ,;
}
if(key == furl){
str
+=<br/>;
}

}
}
if(one==invitezcount||one==invitecount){
str
+=qi+one+=+obj[one]+<br/>;
}
}
}
//固定参数do=charadegarden&action=getinfo&invite=1,fuid为测试时使用,不填 写将调用系统当前登录的用户ID
var url = http://localhost/home/space.php?do=charadegarden&action=getinfo&invite=1&fuid=344;
$.get(url,
function(html){

//获得json并转为对象
if(html){
var obj = eval([+html+]);
GetShow(obj[
0],“”,1)
document.write(str);
document.write(
<br/><br/><br/><br/>);
document.write(
<b>JSON 格式:</b><br/>);
document.write(html);
}

});

</script>

这样,两个无限级的操作就完成了,结果截图:

珍惜劳动成果,虽说内容不 多,可是也是一个一个字打上的,转载请注明!!

[转载]IIS Express简介

mikel阅读(1225)

[转载]IIS Express简介-Scott Guthrie 博客中文版.

原文发表地址]:Introducing IIS Express

[原文发表时间]: June 28, 2010 11:02 PM

当前程序员只能通过下面两种Web服务器之一来开发和测试ASP.NET网站程序:

1. Visual Studio自带的ASP.NET开发服务器(webdev.exe)。

2. Windows自带的IIS Web服务器。

上面两个方案各有优缺点,而且很多ASP.NET程序员告诉我们:“我希望有一个像ASP.NET开发服务器那样容易使用,但是功能又跟IIS一 样强大的服务器”。今天我很高兴地宣布,我们将提供另一个新的、免费的、综合了前两个方案的优点的选择—IIS Express,它的出现使得开发和运行ASP.NET网站程序变得更为容易。

IIS Express支持VS 2010和Visual Web Develop 2010 Express,可以运行在Windows XP和更高的版本上,它不需要管理员权限即可运行,也不要求代码做任何的改动。你可以用它开发所有类型的ASP.NET程序,而且它还支持完整的IIS 7.x功能集。

现有方案

在详细介绍IIS Express之前,我们先来看看ASP.NET开发服务器[i]和IIS的工作方式。

ASP.NET开发服务器

Visual Studio自带的ASP.NET开发服务器(开发代号“Cassini”)的优势在于简练和便于快速启动。它不需要监听远程连接请求(因此在大部分企业 的安全网络环境中都可以使用它),你甚至都不需要使用管理员账号就能启动它,而且它也不要求额外的安装步骤。

正是因为具备启动方便这一 巨大优势,才使得它成为ASP.NET项目的默认服务器,当你在Visual Studio中按下F5时就会启动它。

clip_image002

但ASP.NET开发服务器的缺点是它不提供完 整的Web服务器功能。比如说,它不支持SSL,URL重写规则(例如我在这篇博 客提到的SEO URL重写规则),自定义安全设置,和其它在IIS 7中支持的功能。

IIS Web服务器

用Visual Studio运行和测试程序还有第二个选择— IIS。你可以通过右键单击Visual studio中的Web项目,打开它的属性(在属性窗口中单击“Web”标签)来配置Web工程使用IIS。

clip_image004

使用IIS作为你的开发服务器允许你测试完整的 Web服务器具备的功能(SSL,URL重写规则等)。IIS是一个货真价实的Web服务器—这意味着和在作业服务器上部署差不多开发环境。

但 是采用IIS的缺点是,有些公司并不允许在开发机上安装完整的Web服务器。IIS还要求使用管理员帐号来安装和调试网站。不同版本的Windows又支 持不同版本的IIS。例如,在Windows XP上,你只能使用自带的IIS 5.1—所有IIS 7.x的新功能它都不支持。另外,在VS里配置网站项目使用IIS,还得一些额外的安装和配置步骤。

IIS Express — “鱼”和“熊掌”兼得

我们正在为IIS 7.x加点专为开发场景而优化的新风味,也就是“IIS Express”。我认为它综合了ASP.NET开发服务器易用性的优势和IIS强大的功能。确切地说:

  • 它简练并且易 于安装(文件大小不到10兆而且安装速度相当快)。
  • 在Visual Studio中不需要管理员权限即可启动和 调试程序。
  • 提供了完整的Web服务器功能—包括SSL, URL重写,多媒体支持和所有其他的IIS 7.x模块。
  • 它的可扩展模型和web.config配置结构和IIS 7.x是一样的。
  • 它可以和IIS Web服务器还有ASP.NET开发服务器同时运行在同一台机器上(他们不会相互冲突)。
  • 它支持Windows XP及更高版本的操作系统—在所有操作系统平台上提供了完整的IIS 7.x功能集。

IIS Express(跟ASP.NET开发服务器一样)可以快速地从硬盘上的某个文件夹上启动网站程序。它不需要任何注册或配置步骤。在网站 开发过程中,这个特性使启动和运行网站显得相当便捷。

与VS 2010的整合

我们正在努力使IIS Express在Visual Studio 2010中能够更加容易使用。你将可以配置VS 2010使用它取代ASP.NET开发服务器作为ASP.NET项目默认的Web服务器。跟现在的ASP.NET开发服务器一样,你不需要在IIS Express里注册站点或者虚拟目录。它支持和ASP.NET开发服务器相同的使用方法—只是添加了更多的功能而已。

当你按下F5来 运行ASP.NET项目时,Visual Studio自动启动IIS Express并且使用它来运行和调试网站程序(不需要额外的配置)。跟ASP.NET开发服务器一样,IIS Express运行时,会在任务栏的通知区域显示一个小图标:

clip_image006

右键单击上面的图标并选择“退出”就可以快速关闭 IIS Express。你也可以通过右键单击来列出正在运行的站点列表,还有站点的文件夹路径以及使用的.NET版本:

clip_image008

[译注:IIS Express尚在Beta阶段,暂时还没有中文版,因此将英文原图贴上]

请注意上图中两个很酷的地方:

1. 正在运行的站点“Test Site”,还有IIS Express本身,保存在硬盘的c:\users\[用户名]文件夹中。这就允许非管理员用户使用IIS Express和网站,而且还支持一些现在的IIS服务器没有办法实现的场景(例如可以在受限的企业网络环境或受限的学校公共电脑上运行IIS Express)。

2. 上面正在运行的站点“Test Site”同时支持HTTPHTTPS的访问 方式。IIS Express通过自动安装“自签名证书”来支持URL访问控制列表(URL ACL)和SSL认证,这样程序员(以非管理员用户登录电脑)可以在不需要提升用户权限,或执行额外设置的情况下就可以使用SSL。这个功能允许你在程序 中为SSL设置需要保护的页面(比如登录页面),并且在开发过程中象在真实的web服务器里那样运行和测试这些页面。

IIS 7.x功能集

IIS Express就像你现在已经熟悉的ASP.NET开发服务其那样容易启动和使用。 但是因为IIS Express是基于与IIS 7x相同的代码,你可以用到完整Web服务器的功能集合。这也就意味着你可以像在真实的作业Web服务器上创建和运行你的程序。除了SSL,你还可以使用IIS 7.x的URL重写模块、多媒体插件、动态内容压缩、高级日志功能、自定义安全策略和其他丰富多彩的模块。

除了支持 ASP.NET以外,IIS Express还支持传统的ASP和其他被IIS支持的文件类型—这也使得它成为整合多种技术的站点的理想平台。

总结

我们认为IIS Express使得开发、运行和测试Web程序更加容易。它支持ASP.NET的所有版本和所有应用程序类型(包括ASP.NET Web窗体程序和ASP.NET MVC程序)。最好不过的是—你不需要改动任何代码来使用它。你将可以在所有已有的项目中选用它。

我们马上就会发布IIS Express的beta版。在Beta版中,你应该可以右建单击一个文件夹,并由IIS Express从这个文件夹启动网站程序。今年晚一些时候,我们还会发布一个VS 2010和Visual Web Developer 2010 Express的补丁程序,这个补丁程序将用IIS Express替换掉原来内置的ASP.NET开发服务器。后续版本的Visual Studio会内置IIS Express。

希望这能对您有所帮助。


[i] ASP.NET 开发服务器:即是后图的“Visual Studio开发服务器”,英文原文是ASP.NET Development Server,文件名是webdev.exe。

[转载]一种mvc下处理异常的与http错误的方法

mikel阅读(1337)

[转载]一种mvc下处理异常的与http错误的方法 – 探索、挖掘、研究、致用、创新 – 博客园.

很多网站如果由于用户输入错了地址,出现了如下图的网页

又或者网站的bug导致的应用程序异常,搞出来个满屏的红黄黑,

出现类似情况一定让用户大跌眼镜,个人认为,http错误与应用程序 异常的处理方式应该是我们所需关注的问题。

解决方案

1.定义1个枚举类型用来存储http错误码,与应用程序异常错误

01 public enum DictSystemErrorType
02 {
03 /// <summary>
04 /// 系统错误
05 /// </summary>
06 SystemError = 1,
07 /// <summary>
08 /// 系统异常
09 /// </summary>
10 SystemException = 2,
11 /// <summary>
12 /// 404错误
13 /// </summary>
14 Http404Error = 404,
15 /// <summary>
16 /// 500错误
17 /// </summary>
18 Http500Error = 500
19 }

2.定义SystemErrorCollection静态类用来管理错误提示信息

01 public static class SystemErrorCollection
02 {
03 private static readonly IDictionary<int, string> SystemMsg = new Dictionary<int, string>
04 {
05 {1,"系统错误,请联系管理 员!"},
06 {2,"系统出现异常,请联系管 理员!"},
07 {404,"404错 误,Really very sorry,The page not found!"},
08 {500,"500错 误,Internal Server Error!"},
09 };
10 /// <summary>
11 /// 获取错误提示
12 /// </summary>
13 /// <param name="errCode"></param>
14 /// <returns></returns>
15 public static string GetSystemErrorMsg(int errCode)
16 {
17 return SystemMsg.SingleOrDefault(p => p.Key == errCode).Value;
18 }
19 }

3.mvc下Global.asax文件和webForm下的一样,都继承自System.Web.HttpApplication,

他们都包含Application_Error事件(当应用程序中遇到一个未处理的异常时,该事件被触 发。

定义Application_Error事件处理错误与异常

01 protected void Application_Error(object sender, EventArgs e)
02 {
03 Exception exception = Server.GetLastError();
04 Response.Clear();
05
06 var httpException = exception as HttpException;
07 int errorCode = httpException == null ? (int)DictSystemErrorType.SystemException : httpException.GetHttpCode();
08
09 //记录log  ...
10 //发送错误邮件给网站管理人员  ...
11
12 var routeData = new RouteData();
13 routeData.Values.Add("controller", "Error");
14 routeData.Values.Add("action", "error");
15 routeData.Values.Add("errorCode", errorCode);
16 Server.ClearError();
17
18 IController errorController = new ErrorController();
19 errorController.Execute(new RequestContext(new HttpContextWrapper(this.Context), routeData));
20 }

4.添加 ErrorController与Error Action

01 public class ErrorController : Controller
02 {
03 //
04 // GET: /Error/
05
06 public ActionResult error()
07 {
08 int errorCode = (int)(this.RouteData.Values["errorCode"] ?? DictSystemErrorType.SystemError);
09 ViewData["errorMsg"] = SystemErrorCollection.GetSystemErrorMsg(errorCode);
10 return View();
11 }
12 }

杂谈

为方便管理错误码与提示信息,定义了枚举类型与一个IDictionary字典。

然而同时维护这两个东西着实有些不变,还好可以通过反射取得枚举的提示信息

不过最好把错误提示信息对应错误码持久化到数据库或者xml文件中,然后将其缓存起来。

如此可随时更新错误信息,无需修改程序。

IController是很简单的,它主要的用途在于提供了关于路由的工具来找到控制器并调用执行(Execute)

Controller的HandleUnknownAction:控制器找不到相关的Action将会呼叫 HandleUnknownAction

另外值得注意的是:

mvc下如果你的某个Controller或者自定义基类的 controller重写了HandleUnknownAction方法,

那么出现http错误的话Application_Error将不会 再被执行!


本文源妈

[转载]我的创业体会和大公司的做事比较

mikel阅读(935)

[转载]我的创业体会和大公司的做事比较 – 项目管理 – develop – JavaEye论坛.

工作五年,一晃已年过三十了。读研时,独立做项目,毕业头三年,主要在大公司工作,后来,也就是08年,半创业。具体点,合伙人吧,自己负责IT部门,到 现在6人,公司总共20来人,旅游业。这两年严酷的创业经历,让我越发觉得管理(做事),以及领导(带人、待人,不是管人)的重要性。因为,随着组织的扩 大,混乱度无形中就会增大,管理和领导,就是让这种混乱重归有序,重归单人作战那种意图和行动的高度统一。

说得功利点吧,一个人的财富和其影响力是成正比的。影响力本质上就是对他人的价值。比如,郎_xian_评的出场费一天超过15万。作为技术人员,如果我 们只能影响周边几个人,那么我们凭什么拿那么高的报酬,除非我们做的事情影响了很多人,比如杨勃的豆瓣网。所以,我还是觉得,技术人员往高处发展,逐渐应 该有管理意识、培养自己的管理能力。技术从书本上可以学到很多,管理还真得实践,书上看到的,你觉得很弱智的问题,比如盲目扩张,自己亲身经历时,一样会 犯,也许是行为习惯在起作用,看书不足以改变行为。

回到正题上。
也许是自己曾经在较大公司或团队的做事习惯和视野,刚创业时,用在这种小团队的商业项目开发上,几乎惨败。
先说项目开发这块吧。
大家知道,项目管理和过程管理是两码事,前者关注目标和进度,成本和收益;后者关注做事流程、方法。
项目管理,体会最深的,就是目标和任务分解、进度控制,以及沟通。

项目管理软件
从大公司出来的人,我想最喜欢玩的,就是借助于项目管理软件(核心是甘特图)。市面上的大多数知名的项目管理软件,无论是桌面版还是网页版的,我都试过。 当然最后也选择了一款:ConceptDraw Project,用了一年多,也多少有些用。但最后还是发现,它其实对项目进度和质量关系并不大。也许,一个Excel表格更实用。
项目管理软件,本质上是解决一种沟通和职责分配的问题。比如,一个项目,折叠成一个三层树形结构,老板只关心第一层,也就是整体进度;中间是项目经理关注 的功能层,最后一层,也就是具体的任务,是开发人员关注的。想想,如果没有这玩意,你怎么告诉其它项目干系人进度?但又引出几个问题:
靠文档来沟通,还是靠信任? 太在乎文档,往往导致每天去关注文档如何漂亮、有说服力,并为此花大量时间,而不是项目如何漂亮。另外,是否有文档就可以防止扯皮、兑现承诺?我们是关于 项目目标,还是关注彼此的博弈?

进度偏差 创业型项目,往往都是以前没有接触过,其进度评估往往有很大误差,比如业务需求的挖掘和变化,技术难点,开发人员素质。我们是关注进度,还是关注项目本身 的质量?两者都要,但如何兼顾?虽然有方法学,比如砍掉优先级低的,但你怎么让老板相信某个核心功能就得四天时间。
在我们的进度设计不合理情况下,是否开发人员完成甘特图(WBS)下的任务就ok?远远不够,任务分得太细,往往限制了开发人员的创造性和自我评估能力, 如果提前两天完成呢?
我们现在是以项目管理软件为辅,任务的下达主要以邮件传达,每周一上午的周例会会白板宣布。我发现白板比投影仪PPT好用。

关于规范
这也是大公司特别喜欢玩的。
也许我们前期会制定一个的架构、设计文档,代码规范,这有一个规范建立的时间成本以及规范本书的合理性,再说如果一个开发人员,特别是高手,如果不认同你 的设计和规范,你要强推,他要么走人要么怠工。规范的本质是为了协作和后期可维护,如果只有两个人或一个人写某个模块,你觉得有这个必要吗?规范整洁的代 码,在项目初期,对用户的价值关系很小,你会关心豆瓣网的js代码写得很漂亮吗?我们应该关注代码的健壮性而不是可维护性,我们不是在开发 Windows。

人适应项目,还是项目适应人
大公司,往往是来了一个项目,赶快招人,人来适应项目。小公司呢,我现在的看法是,项目适应人。小公司,往往一个项目做砸,公司就面临解散。所以,我认 为,最好还是按照开发人员的擅长,保证功能质量,最快的速度上线。另外,为了达成进度,可以在上线初期砍掉不太重要的栏目或功能。
我在这个上面栽过跟头的。比如开发人员的擅长,如果他擅长jsp开发模式,而不是Hibernate+Spring的分层开发,就让他按自己的意思做吧。 因为,创业型项目都不会太大;即使技术实现你感觉完美了,用户可能感觉不爽;再说,项目开发,涉及到业务调研、需求分析、原型界面、架构、开发、部署、推 广。开发,也就是代码实现,占去项目时间,也许不到30%。项目如果证实有商业前景,代码重新实现一遍,花不了多少时间。
但我也深深地意识到我们项目管理的级别,就如同CMM1到CMM4。但我还是觉得目前是最好的选择。
如果最低层次的用户需求目标都达不到,直接考虑代码怎么有可扩展性、可维护性,对于小公司就是找死。
另外,尊重和信任、支持开发人员的技术选择,往往是一种激励、增强团队凝聚力的方式。万众一心,比什么目标、进度更有效、实际。
我们应该培养一种团队成员的内部创业心态,而不只是敬业。

在进度把控上,我现在更倾向于强调我们的项目目标和紧迫性,而不是他们的任务。因为我希望大家的关注点是项目,而不是他的上级,他应该对项目负责,而不只 是对上级负责。

说说沟通
项目管理中最难处理好的,就是沟通。以前,我比较关注于工具,如邮件、文档、ppt讲稿会议,逐渐我关注效率和效能,特别是态度。沟通最基础的就是态度。 如果网站上线后,订单提交出现一个核心bug,你是直接找开发人员问责;还是告诉他哪儿出了问题,这个问题的严重,并且自己反省,比如测试流程出了问题。 出现这种事情,也许需要负责人举重若轻的气度。但更深层次的,如果负责人能够培养其员工质量意识,危机意识,才是治本。因为一个有强烈质量意识的团队,他 自然会去对代码健壮性、功能易用性精益求精,自然会去配合测试流程。
刚才那个沟通问题,对开发人员的态度,前者是负面,后者是中立。那么前者,开发人员的反应是如何不让领导下次责怪自己,比如只做领导安排的事情;后者的反 应是怎么去改进,不让这样的事情发生。
如果你认可创新就可能出错,如果你认可开发人员都是想做好的。那么所有的事情,朝正向发展迈出了最决定性的第一步。

沟通:命令式还是征询式
在沟通,特别是下达任务或做决策这类事情上。应该说中国绝大多少管理者都是用命令式。我过去经常在用,但一直在试图改正,改用建议式和征询式。管理者最需 要、最难开口的一句话是:Do you think so?命令的方式,经常出现下级不能理解上级的意图,严重的出现抵触。每个人,其实都喜欢别人按自己的想法做事,但你怎么知道自己的想法或决策是对或不是 偏颇的,怎么让团队愿意去执行?去征询团队其他成员的意见,让他们参与,往往能够培养其主人翁意识、责任感、向心力,还能够完善自己的想法。但要将员工参 与意识,转化为一种习惯,太难。
当大家都没有主见时,需要领导者的果断、魄力和强势,但这种机会并不多,而且这种情况,需要团队成员对领导者的信任。

遵守制度,还是建立信任
在大公司,往往是告诉你怎么去遵守公司制度。在小公司,我认为最基础、最核心的一件事,就是建立信任,让团队信任你的人品(说到做到),信任你的能力(能 够把大家带到一个新的高度)。建立了信任后,下一步的核心工作,怎么将你的个人目标,也就是团队目标,转化为每个成员的个人目标。
有了信任这个基础,才会有了团队建设的第二个核心:激励。
是激励,而不是约束、监督,让团队有战斗力。但大公司往往喜欢后者。也许,大公司都是职业经理人,反正是打工,太关注于事。如果说有个所谓的中国式领导, 我觉得就是以人为本,对人的尊重。人的关系处理好了,事情就好做。
加班、考勤、上网监控,这类对信任、激励极具破坏力的行为,也许是工业型社会对我们这个思考性创造性行业的侵蚀。知识型劳动者,需要一种与体力型劳动者完 全不同的管理模式,这种模式也许需要一个从萌芽、生长到成熟期。现在在目前的中国,还只是刚走出萌芽期。

以前完整看过余世维的11套视频,还看过几遍。他那种人本理念我还是很认同,只是,他在大公司、规范公司的做事情方法和风格,完全照搬拿到小公司,非常危 险。你能够拿幼儿园那种教育方法来教育成年人吗?小公司不具备大公司那种职业化的环境,也不具备大公司在行业中的市场地位及资金实力。
如果说大公司讲究做事方法、流程,如SWOT分析法、BCG矩阵,小公司更看重灵活性、市场适应性。小公司应该适当短视、急功近利,否则在你实施一个三年 计划时,第二年还不赚钱可能就撑不下去。
所以我觉得,在跨国大企业呆惯了,出来创业很危险。一个是做事方法不适应,另外一个就是没有平台。如果要出来创业,以前那种大企业的经历可能更是一种劣 势。 也许有一种情况,你是大公司的高官,拿到一笔很大的风险投资,然后出来创业。

人事招聘
薪水 如果公司给得起,并且应聘者能力差不多。 就不要太在乎那200、300。虽然说至少要不低于行业平均值(IT人员是IT行业平均值,而不是本公司所在的行业平均值),但最重要的,还是不要低于当 事人的期望值,因为最核心的是满意度,而满意度决定于期望值和实际值的差距。对于小公司,往往一个人技术人员的成本和收益,和其工资差距非常大,有可能 10倍。所以,我们的关注点,应该是怎么一开始留住这位人才。然后,怎么让其充分发挥潜力。小公司往往不是因为节省那几千几万的工资成本死掉的,而是充分 利用这位人才才活下去了。

另外,不要以为有多少人才选择的机会,小公司往往不受高级人才的青睐。太高级的人才,可能养不起,而且往往太有个性,很难合作愉快,除非在来公司前有很长 时间的了解。
招聘到合适人才后,应该让其忘掉薪水,专注于工作,寻找工作本身的乐趣。当然,要做到让其在薪水上有优越感,也许是项目很盈利的那一天,开始时很难。

人才标准 如果其能力和你预期相差不大的话,更应该考虑其态度、做事风格,甚至是价值观。因为其能力的发挥,和这个环境,特别是他的直接利益相关者,也就是上司,关 系太大。如果配合得好,一个人可以顶三个。否则,那种内耗导致的进度延期,由此引起的市场机会丧失,公司财力无法支撑,往往是致命的。因为一个几人的IT 团队,每一个人的职责就如同那木桶的一块板,缺了那块都存不了水。
比如关于质量,更确切说是内容质量,我们目前做旅游电子商务,我认为内容质量很核心。但你招进来的同事,始终认为先要量,什么都可以抄,而我强调质,原 创、半原创,可以少而精,而不能多而乱。除开项目进度,怎么去沟通?最好两个人一开始都认同原创的力量。

提升一个人的技能不难,但改变一个人的态度比较难,改变一个人的价值观几乎不现实。所以先找志同道合的人吧。
别期望人才是可替代的。我们不是大公司,我们缺了谁,那一块就不转。
大家都知道,松耦合要付出代价,比如SOAP协议的低性能,AMF私有协议的高性能。创业期,不要太多考虑人才替换,而是关注怎么发挥人的潜力,留住人, 尽快高质量完成项目。人才替换的一个假设,可能是你对自己管理的不自信,因为你不相信自己能够留住人。

这次就写这么多吧。
我似乎有这种体会,考大学、四六级这类资格、证书类考试最好混,因为只要勤奋就可以,再加点方法就可以出类拔萃了。  上班也比较好混,说找工作吧,像我搞技术的,本身对技术很狂热,根本就不愁找不到工作,因为面试时我觉得那家伙应该比我牛,正好可以切磋切磋,没想太多所 以没啥怯场或不自信。工作吧,如果是技术类,特别是商业软件,技术难度都不大,按上司意思来,很容易搞定。创业呢,自己要做商业判断、业务决策,还要协调 若干人的工作(协调的本质是协调利益)。做事和管事,完全是两码事,有些难。不过,创业还是很有意思,因为你可以按自己的意愿去工作去生活,当然也是受限 环境的自由。

我将我的一个回复放在这个地方,特示警醒:

引用
告诫各位处于开发第一线的 朋友,千万不要受本文的误导,把规范和设计文档不当回事。

我的看法:
1、文档的多少和深度决定于项目环境。
如果是大项目,比如二三十开发人员,架构文档、需求文档、代码规范等都是必须,否则开发人员不能迅速了解项目技术和业务特点,从而无法快速开发,也即是规 范可以降低培训成本和团队沟通;另外,项目开发中后期可能根本不可控,谁都看不懂其它人的代码。部署时看到的一些bug无法及时修复,因为到处都有地雷。 我以前经历过这样的项目,最后加班都没用。

如果是产品型,规范更重要。当然我说的产品可能是2.0版以后,因为这时候的产品基本得到了市场的认可。而在初版时,代码写得烂都没关系,因为你不不知道 用户会不会买单,也不知道能否按进度开发完成。而在后续版本,如果没有规范文档,维护的成本都不亚于重新开发。特别是处于一线的开发人员会怨声载道:为什 么要我来收拾残局?那么,这样的士气,开发效率怎么会高,项目质量怎么会高?

2、成熟型大公司那套做事流程,可能高手受不了,但可能是最优的方案。因为,到项目后期维护,往往只是一些业务功能的删减改进,不需要技术高手, 这个过程可能漫漫几年,项目维护成本会非常高,雇佣高手一来他不愿意干二来也不需要这种人,如果项目代码还维持在一种“秩序”,初中级开发人员就可以胜 任,有什么不好呢?
项目上线时,是为了追求利润。项目维护期,是为了省成本。

3、刚入道的朋友,最好是按规范来,就像学武术,先要学套路。否则,养成的编程坏习惯,比如文件名叫Aaa1.java,代码没有缩进。过几年非 常难改。而好的编程习惯,可以提升开发效率,还能让自己思维清晰。
学技术阶段,一定要注意代码的可维护性、健壮性及灵活性,只有养成对代码精益求精的态度,你才可能成为技术高手。技术学好,做技术管理就有了基础,而且别 人也会服你。