[转载]EntLib-DAAB操作指南

mikel阅读(1229)

[转载]EntLib-DAAB操作指南 – .NET 程序花园 – 博客园.

数据访问应用程序块(Data Access Application Block)顾名思义是负责与数据打交道的应用程序块。DAAB并不是整个企业库的最低层,它依赖于企业库的最低层——配置管理程序块。现在简单使用企业库自带的配置工具(Enterprise Library Configuration)来完成DAAB的配置工作。

说到这里想再说些题外话,我们知道企业库是由相对独立Application Block演变而成的,在以前的DAAB我们可能会构建工厂方法模式通过静态方法创建数据库链接。但现在既然企业库已经出炉了,我们也遵循通过配置管理来得到数据库连接这种方法。

概括来说,使用DAAB可以抽象为三个步骤,分别是:

1. 配置DAAB;

2. 实例化数据库;

3. 具体数据操作;


第一部,创建配置信息。如前所述,企业库架构通过配置来创建数据库连接。我们可以使用企业库自带的配置工具(Enterprise Library Configuration)快捷、安全地完成配置工作。具体步骤如下:
准备工作:这步很重要,我们新建了一个Web工程,这个工程得先增加一个项(应用程序配置文件)。这样做是为了直接使用配置工具打开配置文件进行编辑,省去不并要的麻烦。
1.打开配置工具。(我的是XP系统,企业库版本为Enterprise Library – June 2005)开始菜单->所有程序->Microsoft patterns & practices>Enterprise Library – June 2005>Enterprise Library Configuration
2.打开应用程序。File->Open Application 选择工程所在目录下的App.configweb.config文件。


3.新建DAAB。右键点击刚建的Application>New>Data Access Application Block


4.配置DAAB参数。这里内容太多,只说说其中几个常用的配置项。

4.1首先是数据库实例名称,在“Database Instances”节点下,默认为我们建立了名叫“Database Instance1”的实例名,这个名将用于在程序中创建数据库实例,请改成具有标识性的名字。

4.2其次是连接字符串,在“Connection Strings ”节点下,默认也会创造一个,同样,我们也可以改一下名。连接字符串中“Integrated Security”的值默认为True,代表用Windows集成认证(不需要用户名和密码属性),如果我们要用混合认证方式,就要先把值设为False,然后再在所在Connection String中增加用户名和密码两个属性并填充正确的属性值(方法:在具体Connection String节点上右键->New …)。一切就绪便可以确认保存了。

第二部,便是创造数据库实例了。
准备工作:需要把企业库引用到工程中。因为企业库是开源的,所以我们可以直接引用工程或者引用编译好的DLL。为了说明怎样使用,这里只引用两个关键程序集,分别是Microsoft.Practices.EnterpriseLibrary.Configuration.dlMicrosoft.Practices.EnterpriseLibrary.Data.dll。另外为了简化代码的编写,我们再在需要用到DAAB的地方using一下Microsoft.Practices.EnterpriseLibrary.Data这个命名空间。
当我们需要执行数据库操作前,需要创建数据库实例。使用以下语句:
Database db = DatabaseFactory.CreateDatabase(“Northwind”);
就这一条语句便完成了数据库的连接操作!不过要注意,这里的Northwind是上一部配置工作中所述的数据库实例名称,而不是要连接的数据库名。数据库名是在数据库实例里的ConnectionStringNode属性所指向的ConnectionString中,和服务器名,用户名,及密码一起定义的。请把这个关系理清。
另外,Database 的实例,在生命周期内维护着数据库的连接。即当从工厂生产一个数据库实例时,其内部即封闭了ADO.NET Connectionopen方法;而当其生命周期结束后(假定在一对花括号内),它将自动调用close方法关闭连接。

第三部,具体数据库操作。

我们以一具体实例来介绍一下数据库操作:

1.增加对Microsoft.Practices.EnterpriseLibrary.Data.dllMicrosoft.Practices.EnterpriseLibrary.Configuration.dll的引用,并在代码在添加:

using Microsoft.Practices.EnterpriseLibrary.Data;

2.编写存储过程,第二部完成之后在我们工程所要用到的数据库里建立存储过程;

CREATE PROCEDURE GetProductDetails

@ProductID int, //默认为INPUT

@ProductName nvarchar(40) OUTPUT,

@UnitPrice money OUTPUT,

@QtyPerUnit nvarchar(20) OUTPUT

AS

SELECT @ProductName = ProductName,

@UnitPrice = UnitPrice,

@QtyPerUnit = QuantityPerUnit

FROM Products

WHERE ProductID = @ProductID.

4.建立DBCommandWrapper来调用用存储过程GetProductDetails :

string sqlCommand = "GetProductsDetails";
DBCommandWrapper dbCommandWrapper = db.GetStoredProcCommandWrapper(sqlCommand); 
dbCommandWrapper.AddInParameter("@ProductID", DbType.Int32, productID);
//productIDvalue传递给@ProductID 
dbCommandWrapper.AddOutParameter("@ProductName", DbType.String, 50);
//输出的参数 50代表参数的大小。
dbCommandWrapper.AddOutParameter("@UnitPrice", DbType.Currency, 8);

5.执行存储过程:

db.ExecuteNonQuery(dbCommandWrapper);

6.得到输出参数的值:

  string results = string.Format(
CultureInfo.CurrentCulture, "{0}, {1}, {2:C} ",
  dbCommandWrapper.GetParameterValue("@ProductID"),
  dbCommandWrapper.GetParameterValue("@ProductName"),
  dbCommandWrapper.GetParameterValue("@UnitPrice"));

txtResult.Text = results;

为了执行存储过程,上述代码使用GetStoredProcCommandWrapper方法创建合适的command wrapper对象,然后作为参数传递给ExecuteNonQuery方法。

   下面展示一下基类Database6种方法的代码实现  

1. ExecuteDataSet

2. LoadDataSet

3. ExecuteReader

4. ExecuteScalar

5. ExecuteNonQuery

6. UpdateDataSet

(1) ExecuteDataSet方法

Database db = DatabaseFactory.CreateDatabase();

DataSet dsCustomers = db.ExecuteDataSet(CommandType.Text, “Select * From Customers” );

customerGrid.DataSource = dsCustomers.Tables[0];

(2) ExecuteReader方法

Database db = DatabaseFactory.CreateDatabase();

string SQLCommand = “Select top 5 * From Customers”;

DBCommandWrapper dbCommandWrapper = db.GetSQLStringCommandWrapper(SQLCommand);

StringBuilder readerData = new StringBuilder();

using (IDataReader dataReader = db.ExecuteReader(dbCommandWrapper))

{

while (dataReader.Read())

{

readerData.Append(dataReader[“ContactName”]);

readerData.Append(Environment.NewLine);

}

}

txtResult.Text = readerData.ToString();

为了执行SQL语句,上述代码使用GetSqlStringCommandWrapper方法创建合适command wrapper对象,然后作为参数传递给ExecuteReader方法。

(3) ExecuteNonQuery方法前面已经介绍过了(略)

(4) ExecuteScalar方法

Database db = DatabaseFactory.CreateDatabase();

string sqlCommand = “GetProductName”;

int productID=1;

DBCommandWrapper dbCommandWrapper db.GetStoredProcCommandWrapper(sqlCommand, productID);

//单值传参productID dbCommandWrapper.AddInParameter("@ProductID", DbType.String, productID)也可实现 

// Retrieve ProdcutName. ExecuteScalar returns an object, so

// we cast to the correct type (string).

string productName = (string) db.ExecuteScalar(dbCommandWrapper);

txtResult.Text = productName;

Note 其他方法实现请参阅MSDN帮助文档。总结了博客园几位高手关于EntLib的资料,在此我要谢谢他们带我入门 。

[转载]企业库EntLib初识(1)--概述

mikel阅读(1005)

[转载]企业库EntLib初识(1)–概述 – Ryu666’s IT乐园 – 博客园.

今天探讨了一下企业库,觉得真是个好东西,不禁地由心发出感叹:微软你真是可爱……费话少说了,下面就开始吧。

企业库(Enterprise Library)的前身是“Microsoft Application Blocks for .NET”,是微软推出的开源项目,它为企业级开发提供了功能强大的多个应用程序块。以下对它几个特点做一下总结:

可选择---每个应用程序块都保持松耦合,我们可按需要选择性地引用相关程序块。

可扩展---因为它是开源项目,所以它是“可重用,可扩展,可修改”的。我们可以针对自己的实际情况,按照其提供的规范,通过修改原来的程序库或者创建实现相关接口的类来满足需求。

实用、强大---企业库(  Enterprise Library June 2005) 中包括了七个应用程序块(Application Blocks )包括:配置管理应用程序块(Configuration Application Block)、数据访问应用程序块(Data Access Application Block)、安全应用程序块(Security Application Block)、日志及仪表盘管理应用程序块(Logging and Instrumentation Application Block)、加密应用程序块(Cryptography Application Block)、异常处理应用程序块(Exception Handling Application Block)、缓存应用程序块(Caching Application Block)。这七个应用程序块几乎覆盖了企业级架构开发的核心环节。

易用性---对通用的复杂的逻辑进行了封装,只需要简单的调用便可以安全、高效地实现功能。提供了完整、强大的文档。另外,提供了企业库配置工具(Enterprise Library Configuration),让我们“可视化”、快捷、正确地完成完成配置工作。

规范性---企业库是精确地运用Microsoft .NET 的企业解决方案模式思想开发而成。为团队开发提供了一致的设计模式和实施方法的规范指导。

因为时间关系今天就到这里了,因为对于开始写博客没多久的我,这几行字实在是太艰辛了。因为本人水平有限,可能有描述不当的地方,还望High Hand提点提点。晚安!

[转载]企业库EntLib初识(2)--数据访问应用程序块DataAccessApplicationBlock

mikel阅读(1151)

[转载]企业库EntLib初识(2)–数据访问应用程序块DataAccessApplicationBlock – Ryu666’s IT乐园 – 博客园.

数据访问应用程序块(Data Access Application Block)顾名思义是负责与数据打交道的应用程序块,是企业库的重要组成部分。个人认为它相对于其它的应用程序块来讲,应用范围更广,地位更高。(这里 只是做个不恰当的对比,其实每个程序块都同等重要)除非你开发的应用不访问数据库,大概就用不上这个应用程序块了。但我们得记住,它并不是整个企业库的最 低层。它依赖于企业库的最低层——配置管理程序块,这个留到以后再细谈了,现在我只是简单使用企业库自带的配置工具(Enterprise Library Configuration)来完成DAAB的配置工作。

说到这里想再说些题外话,我们知道企业库是由相对独立 Application Block演变而成的,在以前的Data Access Application Block我们可能会构建工厂方法模式通过静态方法创建数据库链接。但现在既然企业库已经出炉了,我们也遵循通过配置管理来得到数据库连接这种方式吧。

首先了解一下DAAB(Data Access Application Block)为我们带来了什么呢?

1--提供了一系列简单有效的方法。让我们通过简单的参数传递便可完成复杂的数据处理逻辑,减少了代码量。
2--数据库透明。对于我们来讲,数据库用的是SQLServer、Oracle还是DB2已经不重要了。通过提供一致的访问模型把各种数据库的数据访问差异封装起来。
3--最佳的ADO.NET实践。因为它是由微软的专职开发小组研发,这个数据访问模型是通过时间验证的,最完整、最高效、最权威的,通过ADO.NET操作数据库的规范。
4--为团队开发提供了一致的、强健的数据访问模型。
……等等。

接下来我们来简单地体验一下如何使用DAAB:
(因为时间关系没有配上图,请见谅)
概括来说,使用DAAB可以抽象为三个步骤,分别是:配置DAAB、实例化数据库、具体数据操作。以后就来展开说明。

第一部,创建配置信息。如前所述,企业库架构通过配置来创建数据库连接。我们可以使用企业库自带的配置工具(Enterprise Library Configuration)快捷、安全地完成配置工作。具体步骤如下:
准备工作:这步很重要,我们新建了一个WinForm工程,这个工程得先增加一个项(应用程序配置文件)。这样做是为了直接使用配置工具打开配置文件进行编辑,省去不并要的麻烦。
1.打开配置工具。(我的是XP系统,企业库版本为Enterprise Library – June 2005)开始菜单->所有程序->Microsoft patterns & practices->Enterprise Library – June 2005->Enterprise Library Configuration
2.打开应用程序。File->Open Application 选择工程所在目录下的App.config或Web.config文件。
3.新建DAAB。右键点击刚建的Application->New->Data Access Application Block
4.配置DAAB参数。这里内容太多,只说说其中几个常用的配置项。首先是数据库实例名称,在“Database Instances”节点下,默认为我们建立了名叫“Database Instance1”的实例名,这个名将用于在程序中创建数据库实例,请改成具有标识性的名字。其次是连接字符串,在“Connection Strings ”节点下,默认也会创造一个,同样,我们也可以改一下名。连接字符串中“Integrated Security”的值默认为True,代表用Windows集成认证(不需要用户名和密码属性),如果我们要用混合认证方式,就要先把值设为 False,然后再在所在Connection String中增加用户名和密码两个属性并填充正确的属性值(方法:在具体Connection String节点上右键->New 过程略)。一切就绪便可以确认保存了。

第二部便是创造数据库实例了。
准备工 作:需要把企业库引用到工程中。因为企业库是开源的,所以我们可以直接引用工程或者引用编译好的DLL。为了说明怎样使用,这里只引用两个关键程序集,分 别是Microsoft.Practices.EnterpriseLibrary.Configuration.dll和 Microsoft.Practices.EnterpriseLibrary.Data.dll。另外为了简化代码的编写,我们再在需要用到DAAB的 地方using一下Microsoft.Practices.EnterpriseLibrary.Data这个命名空间。
说了这么久终于开始进入高潮了,嘿!当我们需要执行数据库操作前,需要创建数据库实例。使用以下语句:
Database db = DatabaseFactory.CreateDatabase(“Northwind”);
就这一条语句便完成了数据库的连接操作,够简单了吧!不过要注意,这里的Northwind是上一部配置工作中所述的数据库实例名称,而不是要连接的数 据库名。数据库名是在数据库实例里的ConnectionStringNode属性所指向的ConnectionString中,和服务器名,用户名,及 密码一起定义的。请把这个关系理清。
另外,Database 的实例,在生命周期内维护着数据库的连接。即当从工厂生产一个数据库实例时,其内部即封闭了ADO.NET Connection的open方法;而当其生命周期结束后(假定在一对花括号内),它将自动调用close方法关闭连接。

第三部,具体数据库操作。
DAAB为我们提供了功能强大的,安全高效的方法,但又是因为时间问题不能每每阐述,在这里只用最直接最简单的方法演示一下它的作用,希望日后有时间再跟大家深入探讨DAAB的各种数据库操作。我们这就来做下通过执行SQL语句取得数据集的操作:
DataSet ds = db.ExecuteDataSet(CommandType.Text,”select * from Employees”);
同样的地,也是一句话搞掂。等到数据库之后要干嘛就留给你自己去想了。

我们可以看出,使用了DAAB以后数据访问改得空前地简单。但DAAB和整个企业库的内含与价值远远不只这些,不过我相信你对它们的热爱会随着了解而逐步加深的。今天就说到这里了,实在太睏了。晚安!

[转载]有助于Flash平台开发提速的50个学习资源

mikel阅读(1018)

[转载]有助于Flash平台开发提速的50个学习资源 – 新闻资讯 – 9RIA.com天地会 – 论坛 – Flash_Actionscript_Flex_Air_开发者社区.

过去六个月里我们已经推出了一大批的产品,有些是最终版本的形式,其他的则是测试版。你无疑需要花些时间来研究AIR2,Flash Player 10.1 for Android, AIR2.5,Flex4,或者FlashBuilder 4的最新特性以加速开发。因此,我以为,把这些资源都罗列在一起以帮助你的学习是个不错的主意。

那么开始咯,无特定顺序。

点对点

当支持点对点的时候,Flash Player 10.1 和 Adobe AIR 2带来了新的功能。最好的当然是我的同事Tom Krcha的了。

•        FlashPlay10.1对多点传送的解释
•        在FlashPlayer10.1用对象复制实现的点对点文件分享
•        点对点GroupSpecifier类的解释
•        Flash Player10.1对直接路由的解释
•        在Flash Player10.1里通过点对点网络小组进行简单对话

Flex和服务器端技术

•        Christophe Coenraets写的关于用Flex和LiveCycle Data Services改善客户端表现的文章
•        Ryan Stewart写的在Zend AMF下使用Flex和PHP创建一个基本CRUD应用的文章
•              如果你用Flex和PHP工作的时候更喜欢使用Flash Builder的数据开发中心特性的话可以看下这篇文章
•        用Flash Builder 4和Eclipse PDT/XDebug调试Flex和PHP项目
•        使用Flash Builder 4建立一个接受用C#写的.NET为基础的web service的Flex应用。

AIR 2

•        理解Flash Player 10.1和AIR2里的安全变化
•        使用AIR2里新的Server Socket访问一个Web Server
•        探索“以默认应用开始的”API
•        探索本地进程API:这里这里这里还有这里
•        寻找在Adobe AIR2里的一列网络接口
•        在Adobe AIR2里建立一个socket server
•        解析在Adobe AIR2里的DNS记录
•        在Adobe AIR2里使用支持远程文件的拖拽功能
•        写多屏AIR apps
•        AIR2和企业
•        利用ColdFusion 9.0.1 ActionScript ORM库开发AIR离线应用
•        建立更佳Adobe AIR应用的十个提示

AIR/Flash Player 10.1 和 Android

•        Android教程里引用的Mark Doherty的P2P视频
•        AIR2.5 StageWebView演示
•        AIR里的地理定位
•        关于使用LiveCycle Collaboration Service和AIR的Android的视频图表
•        使用P2P连接桌面apps的Android apps:这儿这儿
•        如何安装AIR for Android apps的创建工具在AIR里使用摄像头
•        使用Flex和AIR for Android的Android Trader桌面
•              “Android的人声笔记本”:使用Flex,AIR和Microphone API的样例App
•        Serge Jespers的本地安装包的打包工具

Flex 4

•        Flex 4的新东东
•        Flex 3和Flex 4的不同之处
•        Flex 4的皮肤
•        介绍Flex 4(Spark)布局
•        新的动画引擎
•        Michaël Chaize的布局镜面文章
•        用Flash Builder 4风格化Flex 3组件
•        Chet Haase写的图像缩放效果动画滤镜

其他Flash Builder 4相关资源

•        Flash Builder 4里的新东东
•        使用Parsley(还有其他框架)的数据中心特性
•        使用Flash Builder 4和FlexUnit进行测试驱动开发
•        使用Flash Builder的分析器
•        把既有Flex项目从Flex Builder 3移到Flash Builder 4

请慢用。

相关文章:

  1. 用FLASH控制硬件电路版 – 新闻资讯 – 9RIA.com天地会 – 论坛用FLASH控制硬件电路版 – 新闻资讯 – 9RIA.com天地会…
  2. Adobe Flash Player 10正式发布观看Adobe Flash Player 10广告 借助…

[转载]NorthScale Memcached Server的.NET客户端NorthScaleClient使用简介

mikel阅读(998)

[转载]NorthScale Memcached Server的.NET客户端NorthScaleClient使用简介 – dudu – 博客园.

博客园已经全面使用NorthScale Memcached Server,用下来感觉不错。下面简单分享一下.NET客户端的使用方法。

NorthScale Memcached Server官方推荐的.NET客户端是EnyimMemcached。EnyimMemcached有两种方式可以访问NorthScale Memcached Server:

1) Enyim.Caching.MemcachedClient(Enyim.Caching.dll);

2) NorthScale.Store.NorthScaleClient(Northscale.Store.dll)。NorthScaleClient 是基于Enyim.Caching针对NorthScale Memcached Server专门开发的客户端。

这两个客户端的主要区别在于:

1)Enyim.Caching.MemcachedClientt只能访问NorthScale Memcached Server的默认Bucket(默认访问服务器11211端口);

2)NorthScale.Store.NorthScaleClient只能访问NorthScale Memcached Server(默认访问服务器8080端口)。

这里主要讲一下NorthScaleClient的使用方法:

1. 下载NorthScaleClient,下载地址:http://github.com/enyim/EnyimMemcached/downloads

2. 在项目中添加对Northscale.Store.dll的引用。

3. 在web.config中添加相应的配置:

a) 在configSections中添加下面的配置:

<section name="northscale" type="NorthScale.Store.Configuration.NorthScaleClientSection, NorthScale.Store" />

b) 在configuration中添加如下的配置:

<northscale> <servers> <add uri="http://localhost:8080/pools/default" /> <add uri="http://localhost:8080/pools/default" /> </servers> <socketPool minPoolSize="10" maxPoolSize="100" connectionTimeout="00:00:10" deadTimeout="00:02:00"/> </northscale>

注:如果只有一台Memcached服务器,要在这里填两个相同的uri,不然调用时会报错。

4. 客户端调用示例代码:

public class EnyimMemcachedProvider : ICacheProvider { private static NorthScaleClient client; static EnyimMemcachedProvider() { try { client = new NorthScaleClient(); } catch (Exception ex) { log4net.LogManager.GetLogger("cnblogs").Info("EnyimMemcachedProvider", ex); } } #region ICacheProvider Members public void Add(string key, object value) { if (client != null) { client.Store(StoreMode.Set, key, value); } } public void Add(string key, object value, int cacheSecond) { if (client != null) { client.Store(StoreMode.Set, key, value, new TimeSpan(0, 0, cacheSecond)); } } public object GetData(string key) { if (client == null) { return null; } return client.Get(key); } public void Remove(string key) { if (client != null) { client.Remove(key); } } #endregion }

需要注意的地方:

1) 由于创建一个NorthScaleClient的对象成本很高,所以这里要使用静态变量private static NorthScaleClient client。

2) 在NorthScale Memcached Server停运时,NorthScaleClient会抛出”none of the pool urls are working.”的异常,而不是写入日志,这样会影响网站的正常访问。建议在静态构造函数中创建NorthScaleClient对象,在创建对象失败 时捕获异常并写入日志。

在高性能.NET Web应用开发中,缓存是很重要的环节,之前.NET平台缺少好的缓存解决方案,而NorthScale Memcached Server+EnyimMemcached客户端让我们眼前一亮。

由于刚接触NorthScale Memcached Server,理解还不深,这篇随笔写得也很简单,只是希望能抛个砖,希望园子里有更多朋友分享与探讨在缓存方面的心得。

相关随笔:

介绍一款Memcached服务器软件:NorthScale Memcached Server

博客园已经用上NorthScale Memcached Server

[转载]web开发注意事项

mikel阅读(1185)

[转载]web开发注意事项 – 熊哥 www.relaxlife.net – 博客园.

一、Web开发注意事项

l 输出JSON问题。

拼接JSON字符串,有些特殊字符需要替换掉源代码:

public static string ToJson(this string s) {

StringBuilder sb = new StringBuilder();

for (int i = 0; i < s.Length; i++) {

char c = s.ToCharArray()[i];

switch (c) {

case ‘\”‘: sb.Append(“\\\””); break;

case ‘\\’: sb.Append(“\\\\”); break;

case ‘/’: sb.Append(“\\/”); break;

case ‘\b’: sb.Append(“\\b”); break;

case ‘\f’: sb.Append(“\\f”); break;

case ‘\n’: sb.Append(“\\n”); break;

case ‘\r’: sb.Append(“\\r”); break;

case ‘\t’: sb.Append(“\\t”); break;

default: sb.Append(c); break;

}

}

return sb.ToString();

}

如果未替换这些特殊字符生成的JSON不会被正确解析。

推荐使用NET提供的JavaScriptSerializer对象生成JSON字符串。

源代码:

public static string ToJson(this object obj){

return ToJson(obj, null);

}

public static string ToJson(this object obj, IEnumerable<JavaScriptConverter> jsonConverters) {

JavaScriptSerializer serializer = new JavaScriptSerializer();

if (jsonConverters != null) serializer.RegisterConverters(jsonConverters ?? new JavaScriptConverter[0]);

return serializer.Serialize(obj);

}

l XXS攻击。

这篇文章很全面:http://ha.ckers.org/xss.html

数据从数据库读出来显示的时候一定要过滤。源代码:

public static string UnHtml(this string htmlStr) {

if (string.IsNullOrEmpty(htmlStr)) return string.Empty;

return htmlStr.Replace(“\””, “\\\””).ShowXmlHtml().Replace(” “, “&nbsp;”).Replace(“\n”, “<br />”);

}

public static string ShowXmlHtml(this string htmlStr) {

if (string.IsNullOrEmpty(htmlStr)) return string.Empty;

string str = htmlStr.Replace(“&”, “&amp;”).Replace(“>”, “&gt;”).Replace(“<“, “&lt;”);

return str;

}

存在此漏洞别人可挂马/记录键盘操作,并发送到指定的信箱/可弹广告/==

l Cookie/Session/ViewState数据存放需要加密。

代码请参考:

C#.NET COOKIE操作 KEY使用MD5加密,Value使用AES加密

http://www.cnblogs.com/livexy/archive/2010/07/05/1771286.html

C#.Net Session操作 Key使用MD5加密,Value使用DES加密

http://www.cnblogs.com/livexy/archive/2010/07/05/1771288.html

C#.NET 加密解密:AES/DES/Base64/RSA/MD5/SHA256

http://www.cnblogs.com/livexy/archive/2010/07/05/1771285.html

l 上传图片漏洞。

这种漏洞的主要解决办法是:让图片目录无执行权限。

将图片目录放在非站点下。建一个独立站点指向图片目录。

IIS7处理程序映射:

IIS6需要设置:只读和无执行权限。禁用所有Web服务扩展。

l 备份文件时的小漏洞,备份重要文件时扩展名不要使用.bak/.txt之类的,防止别人下载。备份文件请不要上传到服务器。

l 登录漏洞

验证用户是否登录最好的作法是,先按登录名,从数据库查询到用户的ID和密码。在代码里比较登录密码是否正确。不要直接查询用户名和密码在表里是否存在。

l 防止页面被另存为

HTML代码里加:<NOSCRIPT><IFRAME SRC=”*.html”></IFRAME></NOSCRIPT>

l 防止页面被内嵌到别人的网站

HTML代码里加:<script>if (self != top) { top.location = self.location; }</script>

l 防止从本地提交数据

POST/GET数据的时候,我们需要验证页面的referrer。如果referrer非本地的域名不让访问。这样可防止从其它站点或本地提交数据。

l 前台JS验证数据,提交到程序里也需要验证数据。做双保险才安全。

l 防无限刷新

数据提交的时候让按钮变灰不让再次单击。

提交成功后,用JS提示操作成功,并转向新页面。这样做每次对方刷新都要点确定按钮。可以减少刷新次数。

l 防无限提交数据/ajax自动提交数据

加入验证码和提交时间(如一分种发信息)限制功能。

l

二、数据库开发注意事项

l 删除sa用户,新建一个权限为sa的用户,用户名和密码一样要复杂。以防暴力破解。新建一个web连接用户,去掉所有服务器角色,在用户映射中加入此用户要操作的数据库db_ownerdb_public身份。如果需要其它操作要另加权限(如只加insert/delete/select/update)。

l

l SQL注入

存在SQL注入很严重。db_public身份一样能对数据库表有添加/修改/删除权限。

推荐使用Parameter传值,不要使用字符串拼接。

如果需要拼接需要过滤一些特殊字符,代码如下:

public static string SafeSql(this string str) {

str = str.IsNullOrEmpty() ? “” : str.Replace(“‘”, “””);

str = new Regex(“exec”, RegexOptions.IgnoreCase).Replace(str, “exec”);

str = new Regex(“xp_cmdshell”, RegexOptions.IgnoreCase).Replace(str, “xp_cmdshell”);

str = new Regex(“select”, RegexOptions.IgnoreCase).Replace(str, “select”);

str = new Regex(“insert”, RegexOptions.IgnoreCase).Replace(str, “insert”);

str = new Regex(“update”, RegexOptions.IgnoreCase).Replace(str, “update”);

str = new Regex(“delete”, RegexOptions.IgnoreCase).Replace(str, “delete”);

str = new Regex(“drop”, RegexOptions.IgnoreCase).Replace(str, “drop”);

str = new Regex(“create”, RegexOptions.IgnoreCase).Replace(str, “create”);

str = new Regex(“rename”, RegexOptions.IgnoreCase).Replace(str, “rename”);

str = new Regex(“truncate”, RegexOptions.IgnoreCase).Replace(str, “truncate”);

str = new Regex(“alter”, RegexOptions.IgnoreCase).Replace(str, “alter”);

str = new Regex(“exists”, RegexOptions.IgnoreCase).Replace(str, “exists”);

str = new Regex(“master.”, RegexOptions.IgnoreCase).Replace(str, “master.”);

str = new Regex(“restore”, RegexOptions.IgnoreCase).Replace(str, “restore”);

return str;

}

要使用sp_executesql不要使用exec

l select/insert/delete/update操作时需要注意:

当前用户只能查看,添加,删除,更新自己的数据。在操作的时候一定要判断是不是指定的用户可以操作。有很多新人写程序的时候没有做过处理,会出现当前用户可以删除别人的数据。

删除我的博文ID=1SQL代码:delete from blog where blogID=1这样是有问题的,应该这么写:delete from blog where blogID=1 and userid=当前登录的用户ID。同样查询,修改,更新也存在这样的问题。

l

三、自我测试需要注意

l 想象每一个函数/方法调用尽可能存在的情况。

l 测试所有按钮/链接/图片是否可点,链接地址是否正确,是否存在无效链接。

l 每一个可输入控件都要输入特殊字符去测试。每一个URL参数都要输入特殊字符去测试。

特殊字符:

or 1=1

or 1=2

<script>alert(1)</script>

”%’ or 1=1;–

\r\n

\n

l 界面是否美观/颜色是否刺眼/

[转载]N95当手柄玩游戏,甩起来还可以用手势控制电脑,分享一下我们的设计经验

mikel阅读(1173)

[转载]N95当手柄玩游戏,甩起来还可以用手势控制电脑,分享一下我们的设计经验 – Loning’s home – 博客园.

说明

演示视频

http://share.renren.com/share/259556761/2861373448

http://share.renren.com/share/259556761/2861370739

使用说明

解压缩后请打开Driver文件夹,按照自己的系统进入文件夹,其中x8632位系统、x6464位系统。

执行devinstall.bat后将会向系统添加模拟手柄的驱动程序。

执行devremove.bat将会删除手柄驱动。

执行完毕后请通过 设备管理器 (我的电脑》右键》管理》设备管理器)查看一个名为Wiisio Joystick Hid Device的设备,如果该设备工作正常,则系统成功安装该设备。

添加完驱动后即可直接运行Wiisio目录下的Wiisio.MainWinProgram.exe

可以使用test\joytester.exe 测试模拟手柄的运行情况。

在手机上,使用C(删除键)来切换模式,可以通过手机菜单切换模式

Joystick模式下

手机数字按键1:将当前位置设为手机的平衡位置。

GameKeyboard模式下

手机数字按键1:将当前位置设为手机的平衡位置。

手机Shift键(C删除键左边):切换当前的映射文件。

GestureControl模式下

手机Shift键(C删除键左边):切换当前的映射文件。

系统需求

.net framework 2.0 sp2以上的.net框架

Microsoft or Widcomm Bluetooth stack 微软或Widcomm蓝牙协议栈

Nokia N95(其他带传感器的手机因无手机设备未测试)

开发人员

Loning

整体程序设计、驱动开发

Sherlock Yang

Nokia N95客户端开发

Reyoung

硬件设计

功能概述

手势识别

手势模拟按键

手势运行程序

新手势训练

游戏模拟

通过重力模拟游戏手柄,映射手机按键

通过重力模拟上下左右wsad等按键,映射手机按键

控制设备

通过串口发送控制信息

开发计划

iPhone, M9, Windows Phone, Windows Mobile 等客户端开发

使用Wifi通信

公布通信协议接受第三方手机客户端

下载地址

http://u.115.com/file/f1f602a133

摘要

中文摘要

本系统基于手势识别,Windows驱动模拟,八向全驱四轮机器人控制技术,蓝牙无线通信以及无线电通信技术,提出了通过对重力加速度进 行分析处理进而实现数字控制,并以此为控制信息实现一个以EMB-4650开发板为中心,集娱乐,生活为一体的多方位控制解决方案。通过远程手机客户端提 供重力加速度数据,EMB-4650开发板进行分析处理提取相关信息并发送给相关设备,从而实现以一手机客户端控制多个设备行为的目的,进一步展现一机多 用低碳环保的社会主题。

Abstract

The system is based on gesture recognition, Windows-driven simulation, eight-wheel drive to the whole robot control technology, Bluetooth wireless communications and radio communications technology, put forward a solution that use the result of processing the gravities’ data to achieve a multi-directional control solutions that use the EMB-4650 and make life, entertainment together. Remote mobile client provides acceleration of gravity data, EMB-4650 development board analysis and processing extract relevant information and send the information to the related devices, so as to realize the goal that use one mobile client to control multiple devices, to show the theme of a multiple purposes and Low carbon and environmental protection.

系统概述

系统简介及开发背景

现阶段,单一简陋的系统控制方法已经无法满足人们对系统快速准确控制的需要,于是触屏、语音控制、基于图像的手势控制、游戏摇杆等技术应运而生。然而,对于一些特殊应用,需要特殊的控制方法。

本文提出了一套基于加速度分析的控制系统解决方案,实现了一个一机多用、节能低耗、多设备同时处理、具有多种控制模式的控制系统。该系统可以在用于实时手势控制的同时播放电影、进行简单的游戏娱乐。

系统的整体架构分为四层:第一层是数据采集层,第二层是数据分析层,第三层是命令映射和控制层,第四层是被控制设备层。

“数据采集层”的主体为重力加速度信息提取模块,通过远程手机客户端采集加速度信息,并通过蓝牙发送到EMB-4650开发板。现在手机已经相当普遍,拥有重力加速度传感器的手机也已很多,这为此解决方案提供了良好的设备应用基础。

“数据分析层”的主体为数据分析模块,通过SVM支持向量机以及其他数据处理方法对加速度信息特征进行提取。

“命令映射和控制层”的主体为动作映射模块,通过第一层提供的加速度数据特征以及预先设置的映射表和当前系统所处的模式,将相应的加速度数据特征映射成相应的命令,并发送给相应的设备。如需修改映射表,进入本系统的用户界面设置界面对相应的数据特征设置相应的命令。

“被控制设备层”主体是相关设备接收命令以及执行命令的模块,此层范围很广,如本解决方案中的基于单片机的四轮机器人,这个机器人可以通过 接收到的命令实现各种操作(如加速,刹车,转弯,复位等)。通过连接不同设备至机器人下方的的扩展接口可实现拖地、扫地、吸尘等功能(扫地、吸尘功能未实 现);再如家庭中线路开关,可以接受到控制信号实现闭合控制。第四层与第三层主要通过无线模块进行通讯。

应用前景

该解决方案提供了一种既直观明了、充满想象力和乐趣的系统控制方法。使用该控制系统,用户不必在漆黑的夜晚摸黑找电灯开关,只需用已设定 好的手势摇晃用户随声携带的手机就能使眼前一片光明;用户也不必为与同伴一起玩游戏时找不到手柄而发愁,掏出你的手机,这就是一个拥有完整功能的游戏手 柄;用户甚至能掏出你的手机,控制机器人帮助他做家务。该解决方案基于EMB-4650开发板的节能嵌入式系统,并且还应用了特殊的节能技术,即在没有数 据处理时降低优先级以节省能源,在有数据处理时提高优先级以相应实时数据处理,因此完全可以将本系统作为24小时全天候家庭控制系统。现今,手机的极其普 遍,iPad等设备也在逐步增多,加速度传感器已逐渐成为这些设备的标准配置,该解决方案具有相当广泛的应用面。

系统特点

该系统采用加速度信息进行手势识别及系统控制,识别率高,外界环境干扰性小。

新手势易于添加,手势样本需求量小,训练迅速。用户可通过软件附带的训练功能添加自定义手势。

系统能够支持多种不同设备的连接。

充分利用现有资源、提高资源利用率、绿色节能,例如重力加速度信息使用手机等设备、电器控制也仅需在插线板多加一层我们设计的开关。

系统资源占用率低,在系统运行的同时,该设备可处理其他事物,一机多用。自动调节系统程序在操作系统中的运行优先级,无远程设备连接时降低优先级来降低系统资源使用率,节省电能;有远程设备连接时提高优先级,实时处理数据,避免丢包。

系统将加速度信息分为时序性数据与非时序性数据,时序性数据是会对下一次数据处理产生影响的数据,非时序性数据则与之相反。系统将使用不同线程处理这两种不同类型的数据。

系统原理及方案

系统设备组成

主体部分
EMB-4650开发板 1个
Broadcom 2046 Bluetooth 2.1 1个
AT89S52 单片机 1个
FSK-21 超再生无线发射接收模块 1个
标配电源 1个
串口数据线 1条
自制机箱 1个
机器人部分
带有47:1减速齿轮的电机 4个
舵机 4个
带有橡胶防滑套的车轮 4个
6V 210mAh镍氢电池组 1个
PVC底盘 1个
塑料接插件 若干
有机玻璃板 若干
AT89S52 单片机 1
FSK-21 超再生无线接收模块 1
远程设备
Nokia N95 1
Nokia 5230 1

系统架构

图 3.1 系统整体架构

功能与指标

  1. 实时加速度信息处理

加速度信号在模拟键盘、手柄操作时需要较高的实时性,因而需要将时序性数据处理与非时序性数据处理分离。

  1. 手势识别

可进行离散的手势识别,易于添加新手势,用户可选择不同的手势-功能映射方式,手势识别率在85%以上。

  1. 多远程设备连接

支持多种不同设备同时连接,程序易于二次开发。在处理多设备时系统应保持实时性。

  1. 机器人模块

机动灵活的机器人模块,最高移动速度为5.4km/h。且转弯灵活。可以实现前进、后退、转弯。另外配有大容量电池组,可以在无电缆供电的情况下长期运行。采用低频率无线遥控技术,有效提高通信距离,降低能耗。在无障碍条件下,通讯距离长达10米。

  1. 无线通信模块

系统与被控设备之间通过无线设备进行通信,考虑到该设备工作环境主要位于室内,因此无线信号范围应在半径10M左右,但具有较好的墙体穿透能力。

  1. 无线开关

无线开关是一种被控设备,进行简单的电路闭合控制。该设备应易于安装,能长期稳定运行。

  1. 用户界面友好

操作界面简洁、易用。

  1. 节能低耗

系统空闲时CPU平均占用率不超过1%,每个设备连接后CPU平均占用率的增长不超过5%。

硬件系统设计方案

硬件部署

图 3.2 硬件部署图

被控设备原理图

图 3.3 Wireless 315MHz 无线信号发射部分

图 3.4 Wireless 315MHz 无线信号接收部分以及四轮机器人控制

软件系统设计方案

软件流程

图 3.5 主程序流程图

软件架构

整个系统分为4层:

(1) System Layer:该层包含系统API,Bluetooth驱动,串口操作模块,以及系统自定义的手柄模拟驱动。

(2) Library Layer:该层包含程序依赖的各种库。其中SVM.net (1)是一个 .Net 平台下的libsvm (2)实现,Math.net (3) 是一个开源数学库,ZedGraph (4) 是一个绘制图表曲线的开源库,Wiisio.Sensors 是系统实现的加速度信息处理库,HID Library (5)是一个访问HID设备的库。

(3) Logic Layer:该层是系统的主要逻辑层,负责接收、处理加速度信息以及分发数据。

(4) User Interface Layer:该层负责系统与用户间的交互。包括设备监视器、手势训练工具、任务栏通知及系统设置。

系统实现

重力加速度信息提取

手势识别

运动监听

在系统与远程设备连接后,系统会源源不断的接收到加速度信息。系统首先需要分析出该设 备是否处于运动状态。由于三轴加速度传感器的不稳定性、用户手部轻微的抖动,系统所获得的三轴加速度信息并不稳定,但在一个窗口时间t内,所获得样本的方 差总小于阈值。通过改变该及窗口时间t,即可设置系统检测设备运动状态的灵敏度。

在设备由静止转为运动的过程中,由于运动起始时设备会获得较大的力,从而引起加速度信息的剧烈改变,因而在一个较短的窗口时间内,即可检测出设备的运动情况。

在设备运动的过程中,由于设备会出现匀速状态,在t时间之内,样本的方差会小于,从而会导致系统判断错误。但作为手持设备,由于手部运动范围限制,只要合理设置匀速状态下窗口时间t’,即可降低系统误判率。

加速度信息归一化

不同的用户做同一个动作时时间与幅度可能相差很大,同一用户做手势时也可能出现上述情 况,因此需要将样本数据归一化。以三轴加速信息中一轴数据样本A进行说明:(1)减去初始加速度(重力)后,对样本A进行线性样条插值,得到一多边形曲 线。(2)以样本数对加速度信息进行间隔相同的采样得到样本数据A’(3)对A’进行三次样条插值得到二阶参数连续的三次样条曲线。(4)对样本A’进行 统一帧数的采样得到标准长度的样本数据。

经过归一化处理后,样本噪声明显降低。所得平滑曲线如图 4.1所示,其中红色为拟合后曲线,黄色为原始数据。

图 4.1 加速度曲线

特征提取

在对样本数据归一化后,可得到四组样本A1、A2、A3 以及总的加速度Ac。分别计算样本的均值、方差、顶点数目、丰度、偏度。通过直方图,将各点由最大值至最小值分为若干组,统计落在组中的点的个数。

由于三组样本相互独立,我们可以构建一个包含该运动信息的高维特征向量V。然后通过支持向量机对这些向量进行分类处理,实现手势识别。

支持向量机与手势识别

SVM 的最终决策函数只由少数的支持向量所确定,计算的复杂性取决于支持向量的数目,而不是样本空间的维数,这在某种意义上避免了”维数灾难”。 (6)因此,只要选定了合适的核函数及参数即可对向量V进行分类。

系统采用Matthew Alastair Johnson开发的SVM.NET库 (1)。该库重新实现了台湾大学 Chih-Chung Chang and Chih-Jen Lin开发的libsvm (2),并加入了自动选择核函数及参数的过程。

程序内置手势

系统为了简化用户操作,已经内置了一部分训练好的手势供用户使用。用户可以在该基础上添加新的手势,亦可删除这些手势重新训练。系统内置的手势如图4.2所示。

图 4.2 程序内置手势示意图

类游戏手柄信息提取

系统同样关注远程设备的静止状态。由于设备在旋转过程中是平缓的,而且系统关心的是一长期的状态,而非旋转时一刻的状态,因而可认为三轴所得加速度即为重力。将三轴所得数据组成向量V,对V进行标准化后对各维进行反余弦运算,即可得到远程设备与重力夹角。

得到设备与重力的夹角后,系统需要一个平衡位置来取得类手柄的X轴、Y轴信息。规定X轴为远程设备前后旋转信息,Y轴为远程设备左右旋转信息。在设置平衡位置时,系统将重力分量最大的维度作为Y轴信息来源,将Y轴信息改变时改变量最小的维度作为X轴信息来源。

通过格式化X、Y信息,设置合适的呆区,即可实现手柄的左右、上下(带数值)按键,同样可以模拟键盘的左右上下按键。可用来进行赛车、模拟飞行等等支持手柄以及支持上下左右操作的游戏,亦可用来手工操纵四轮机器人。

多设备连接支持

在系统设计初期,考虑到使系统可以支持多种协议的连接、多种设备的接入,对于今后有可能进行的点都进行了抽象。信号处理模块见图 2。

在每一个IDataReceiver的实例中,系统总有一个线程处于监听状态。在远程设 备连接后,该监听线程将处理该远程设备传来的信息,同时系统将会开启另一线程进入监听状态。考虑到系统理论最多可支持255个设备,在处理加速度信息时, 将要求时序处理的部分用原线程处理、其他部分使用线程池处理,降低在处理复杂数据时丢失加速度及其时间信息的情况,降低系统延迟。

系统支持从不同类型的设备获取加速度信息并进行处理。在远程设备连接后,远程设备上的客户端程序会根据运行的不同环境发送特定格式的初始化消息到服务端。在服务端上以简单工厂模式创建一个可以处理该设备加速度信息的实例,并注册到该远程设备在服务端的代理类上。

在远程设备与系统的网络连接部分类视图中(图 2),DataManager负责远程代理设备与本地设备的映射、事件的统一分发,系统上层结构可通过订阅该类的事件接收所有远程设备的事件,也可通过该类访问远程设备。

通过远程设备特定的初始化请求信 息,ILongLifeObjectProviderFactory将产生一个提供该设备所需处理的消息处理程序工厂,即 ILongLifeObjectProvider。该系统现在主要专注于分析加速度传感器的信息,但Wii的手柄、不久前发布的iPhone 4GS中是内置陀螺仪的,这样的系统设计结构便于系统的扩展。

图 4.3 多设备连接实现类图

动作映射

在系统接收到一个远程设备的连接请求后,系统会创建类型为RemoteDevice的 远程设备代理实例。该类在接收到加速度信息并处理后,会产生一系列的消息报告。消息报告包含基本的消息信息以及相应的处理方法。该处理方法会根据远程设备 的工作模式、消息报告自身的类型将相应的动作传递给分配给该远程设备的已知的本地设备的抽象代理类。DataManager的实例将会订阅所有包含 RemoteDevice消息报告的事件,管理RemoteDevice与本地设备的映射并且调用消息报告的处理方法。

用户可在配置界面选择映射信息。系统将会根据工作模式不同将处理后的远程设备加速度信息及按键信息进行映射。

类视图(图 4.4)所示为系统现有消息类型,他们均由远程设备的代理类产生。其中DataMessage传递远程设备的加速度信息及按键信 息;MovementMessage由类型为IGsensorProcessor的加速度分析器检测到一个运动后由远程设备代理类产生,该消息传递一段离 散的动作信息,由运动开始至运动结束;ModeMessage会将远程设备的工作模式传递给本地设备组。

图 4.4 消息模型

现阶段系统实现了三类手势的映射动作,分别为 模拟按键、运行程序与设备控制。其中运行程序可以启动某一个我们需要的程序,也可打开某一文件、网站。模拟按键实现可以用来模拟操作系统、其他程序的快捷 键功能,也可模拟一段文字的输入。设备控制会将设备控制参数传递给被控设备控制器,由控制器将信息发送到被控设备。

系统维护了一个类型为Dictionary<int, GestureAction>的表,其中Key为手势编码。在系统接收到手势后会查找该表并调用相应GestureAction的DoAction方法,执行该指令。类视图见图 4.5。

图 4.5 映射模块类图

用户界面

监视器

在监视器界面,用户可以查看到已经连接到系统的远程设备以及他们的运行模式、即时加速度信息、按键信息,并且可以选择该设备的手势映射模式、按键映射模式,在选择映射方案后会立即生效。系统会记录某个特定设备的配置信息,在该设备再次连接时会自动读取相应的配置。

监视器的窗体在接收到用户请求后才会被系统创建,在用户选定要查看的设备后,监视器才会 订阅特定远程设备的数据接收事件,并将该设备的基本信息以及加速度曲线显示在界面上。通过以上方法,降低了系统在UI层上的系统资源使用率。在用户更改映 射方案后,远程设备代理类会发出一个包含ModeMessage的事件,DataManager会处理该事件,查找与远程设备配对的本地设备,更改本地设 备的映射方案。

图 4.5 监视器界面

训练界面

该界面用于向系统添加新的手势。在系统检测到新手势时,该界面会弹出一个新的窗体来显 示系统捕获到的手势。为了使操作简便,在编写时加入纯键盘操控的方式,用户可以连续做几组手势,然后完全用键盘操作来增加新手势,简单快捷。同时拥有训练 数据的管理界面,在加入了错误数据后可轻易删除。由于系统分类采用支持向量机进行手势分类,少量错误数据对系统不会产生较大影响。


图 4.6 手势输入界面

图 4.7 手势数据管理界面

任务栏通知

在进行一些操作时,用户不希望查看监视器,同时也不希望观察远程设备上的反馈信息,但需要确认其手势是否被系统识别。因此,系统提供了接受到消息后在任务栏通知区域显示反馈消息的方式。

设置界面

在设置界面,用户可以设置映射配置、系统是否弹出任务栏通知、以及监视器中加速度图像的更新时间。系统设置的读取与存储由系统自动完成,使用.net framework的序列化功能实现。

在设置手势映射的界面中,系统使用了比较成熟的列表控件ObjectListView (7),该控件支持自定义单元格编辑控件、数据绑定以及自定义列的输入输出方法。由于ObjectListView支持动态绑定单元格编辑控件,不同的手 势映射能够在同一个界面中完成,通过多态很容易的实现了这一点。

为了便于热键输入,系统自定义了一个ComboBox。为了避免与系统的快捷键冲突,在输入时用户应逐个按键依次按下,对于系统响应的按键,如Win键,可在下拉的菜单中选取。手势设置界面见图 4.8。

图 4.8 手势映射设置界面

在设置按键映射的界面中,用户可以将远程设备的旋转状态、按键转换为本地的案件信息。设置界面见图 4.9。

图 4.9 按键映射设置界面

基于单片机的四轮机器人

总体设计思路

在机器人设计上,我们采用了模块化的思想。将舵机控制交由专门的舵机控制器执行。剩下控制和通信工作交由51单片机实现。

出于灵活性的考虑,我们并没有采用差速转弯的控制方法,而是利用四个舵机控制方向。这样便可以做到原地转弯或者横向运动。机器人既可以在家中当作娱乐玩具,也可以添加吸尘器等外部设备,做家庭助理。

硬件设计

舵机控制模块

采用高精度8路舵机控制模块,控制脉冲宽度从500ns到2500ns不等,支持匀速切换脉冲宽度,使移动更平滑。切可自由调控波特率,多模块串联等。

该模块通过串口与单片机通讯。达到简化单片机任务,精确控制舵机,消除舵机抖动等不良反应的作用。

无线接收模块

由于我们系统主题通讯为蓝牙通讯,为了防止信号间相互干扰,我们放弃使用2.4hz的无线收发模块。而是采用了成本更为低廉,且不与主要通讯干扰的超再生接受模块(接受频率为315MHz)。其接口如下图显示:

图 4.10 再生接收模块

由于潜在的多设备多指令需求和超再生无线通讯单次通讯位数过少的问题,我们不得不实现了一个无线控制协议。去除校验,控制等因素,可做到91个设备和91个指令的收发。且可扩展性强,可以很轻易的增加设备上限和指令上限。

51系列单片机外部中断为低电平触发,于是我们便加入了非门。将vt得到的接受信息信 号,转化为TTL低电平。同时,为了充分利用非门的端口(74LS04),并加强系统的稳定性,提高信号输出的电流,将数据线同时串过非门,从而得到电流 更强的输入信号。电路图如图4.11所示:

图 4.11 电路图

电机控制模块

电机控制模块采用了l298芯片,该芯片可以用pwm信号控制电机转速。并且采用光电开关,达到了控制信号隔离的目的。

单片机部分

出于通用性和成本考虑,这里选择了廉价的单片机at89s52。虽然8位单片机的运算能力不强,但是对于简单的控制已经足够使用。运算工作,多交由上位机,即intel开发板工作。这种对于运算性能的牺牲,使机器人整体成本功耗下降,更加节能环保。

软件设计

总体思路

由于潜在的多设备多命令需求,软件必须是可以高并行输入,并且对所输入的信息进行及时 响应,于是我们将软件设计为消息驱动的模式,外部事件发生则通过中断产生一个消息,且将消息压入队列。而当系统空闲时,便从消息队列中取出响应消息,进行 处理。这样既在原有单核心单线程的硬件基础上达到了接受并行信号的目的,也充分的发挥了单片机的性能。

虽然51系列单片机开发语言为C语言,但是在软件设计上,我们运用了更多面向对象的设计思想。其结构图4.12所示:

图 4.12 单片机软件类型设计

具体模块介绍

CQueue是一个循环队列,由于此类处于系统的核心位置,对其算法进行优化,是之效率更高。

Message主体实现消息队列,实现收到消息,获取消息功能。

Serial 由中断实现串口通讯,并且方便调试,实现了一些串口输出函数,例如输出字符串,输出二进制数等。

WirelessControl实现了多设备多命令的系统的收发协议。

CarControl 针对四轮机器人实现的机器人运动控制函数

SwitchControl 远程电源开关控制函数

消息驱动的系统

图 4.13 消息驱动系统

如图4.13所示,整个系统以消息为中心。此图反映了消息处理流程,系统反复查找新消息,如果有新消息,便去做响应的处理。这种系统扩展性强,便于添加新设备和新的控制任务。

由中断实现的并行输入

我们的系统输入为中断输入,并且只在输入中断中做最简单的消息处理和发送消息,使系统可扩展性,鲁棒性都得到增强。并最大程度的利用了系统资源。

图4.14是串口中断的活动图,内容极为简单,只是将串口数据存下并发送消息给消息队列。

图 4.14 并行输入示意图

图4.15是无线中断活动图,在无线中断中,我们对收发数据进行编码分析,但是其实也是仅仅做了解析无线内容,发送无线消息的任务。而消息的处理,依赖于主消息循环中的处理函数处理。

图 4.15 无线中断活动图

这种中断处理的简单化,有利于大量并行数据的输入。

与主题分离的控制模块

我们的系统可以实现各种控制模块,且可以自由扩展。控制模块接受主体的消息,解析消息和处理消息的工作,完全交由控制模块自己进行。方便扩展。

无线发射及协议

无线发射协议:

每帧四位,且0000位无效

起始帧,一帧,四位分别为1111。

地址帧,两帧,每帧介于0001到1101之间。

数据帧,两帧,每帧介于0001到1101之间。

结束帧,一帧,四位分别问1110.

总体示意图如图4.16:

图 4.16 无线发射协议

无线发射流程:

由串口写入发射命令,例如”A0D0*”,其中A后面的数字为设备地址,D后面的数字为发射数据,*是命令截至位。

串口命令写入解析完毕后,调用发射无线消息的函数,按照消息格式进行发送。

通信模块

远程设备与开发板的通信

我们选择Bluetooth 技术来完成远程设备与开发板之间的通信。

Bluetooth 技 术在2.4 GHz 波段运行,该波段是一种无需申请许可证的工业、科技、医学 (ISM) 无线电波段,能耗低,可以使远程设备更加持久的发送加速度信息。支持点对点及点对多点通信,因而在设备主板上仅需要一个Bluetooth模块。现阶段附 带有重力传感器的手持设备通常都具有Bluetooth 模块。

系统定义了与远程设备间通信的消息格式。Bluetooth是全双工模式的,系统接收到加速度信息并处理后,会将处理的结果回发到远程设备,再由远程设备通过不同的方式展现出来。因此,在不外接显示设备的情况下,系统依然可以良好工作。

被控设备与开发板的通信

操作系统驱动

Windows系统没有提供模拟手柄输入设备的API接口。因此,需要开发一款驱动程序,接收程序处理好的手柄模拟信号,再将该信号传递给操作系统。

系统所需驱动为Joystick Driver,是一款在Human Input Device(HID)类下的驱动。该驱动是通过Report与系统、外界程序交互的。考虑到手柄驱动支持反馈,可以在报告定义中,定义一种Output Report,得到Output Report后将信息写入驱动缓冲区,在系统请求Input Report时将缓冲区的信息读出即可。由于在报告定义中没有值是跨字节的,并且缓冲区仅在驱动接受到Output Report后被改写,因而在写入缓冲区时无需考虑线程同步问题。

该驱动采用WDM模型编写,参考了WDM SDK中自带的VHIDMINI驱动程序。可支持带数值的X、Y轴,油门(Z轴),帽子开关(观察点),及12个模拟按钮。

远程设备客户端

远程客户端平台

解决方案采用手机移动平台,考虑到当今社会手机已经相当普及,所以想通过在手机端进行开发,以达到一机多用的效果,同时也符合当今社会低碳环保的社会主题。

在决定采用手机移动平台之后,就需要对众多手机平台进行选择,我们决定选择Symbian平台作为我们解决方案的远程客户端平台。Symbian的优势在于它得到了占据市场份额大多数的手持通讯设备厂商的支持,在NOKIA的大力倡导下,已经成为一个开放的、易用的、专业的开发平台,支持C++和javaSymbian的优势在于它得到了占据市场份额大多数的手持通讯设备厂商的支持,在NOKIA的大力倡导下,已经成为一个开放的、易用的、专业的开发平台,支持C++java语言。Symbian作为一款已经相当成熟的操作系统,拥有着很长的发展历史,同时也拥有着很好的开发资源,以及开发文档,更加便于我们对其进行开发。

蓝牙模块

Symbian OS上的蓝牙模块是由一系列的组件层次结构组成,就是一种堆栈结构,其总体结构见图 4.17。

图 4.17 蓝牙模块层次结构图

Symbian OS上的蓝牙API能够使应用程序访问蓝牙的操作,对Bluetooth Host Controller组件,属于底层结构,应用程序无法直接访问,对于Bluetooth Host 组件,应用程序可以通过一些特定操作实现对蓝牙设备的访问。本解决方案的蓝牙模块主要可以分作三个部分:

  1. 蓝牙设备发现模块

此模块用于设备的选取,即选取要连接的服务端,设备发现模块选择使用 Symbian OS提供的Bluetooth UI(Bluetooth Device Selection UI),其提供一个对设备选择的简单解决方案,由系统写好的相关界面实现用户对设备的选取,首先会提供使用较多的蓝牙设备供你选择,如果你的设备在此中间 就可之间减少蓝牙设备寻找的时间,如不在其间也能对周围蓝牙设备进行重新的搜索。蓝牙发现模块的界面如下:

  1. 蓝牙服务发现模块

此模块用于对服务端的服务进行发现,并获取相关蓝牙服务信息(如port通道数),由于本解决方案实现了多点连接,并自定义了一个基于RFCOMM的通信协议,所以此模块的作用至关重要。服务发现模块的类图见图 4.18:

图 4.18 服务发现模块的类图

蓝牙服务发现模块首先通过上面介绍的 Bluetooth Device Selection UI向用户询问要连接的服务端,然后通过Symbian的SDP数据库创建需要寻找的服务的记录(在本例中是自己定义的基于RFCOMM的协议),接着就 通过agent在服务端开始相关服务的寻找,并记录下蓝牙服务的相关信息(在本例中是port通道数,如果找到相关服务的话),蓝牙服务发现过程的流程见 图 4.19。

图 4.19 蓝牙服务发现过程

  1. 客户端模块

在实现了对需要连接的服务端以及服务端上服务的查找之后,接下来就是需要在 手机客户端与服务端之间建立连接,实现相互之间通讯,以将手机上的采集的重力加速度数据传送到服务端加以分析计算,并将结果回发给客户端,使客户端能根据 计算结果采取不同的反馈。客户端模块的类图如图4.20所示(仅展示了蓝牙部分):

图 4.20 客户端模块类图

客户端工作的实际过程:首先通过蓝牙服务发现模块(其又通过蓝牙设备发现模块)获取设备的相关信息,然后用Symbian提供的RSocket建立客户端与服务端的连接,客户端就进入工作状态。客户端的状态图见图 4.21。

图 4.21 客户端模块状态图

重力加速度获取模块

手机端对重力加速度的获取通过Symbian OS提供的RRSensorApi,此API采用观察者模式,并且提供给程序访问重力加速度数据的接口,对外提供一个 MRRSensorDataListener的接口,只要继承此接口,并重写其对重力加速度数据处理的虚方法,再将其注册进其重力加速度获取实例便能获取 到重力加速度数据,其过程还需提供硬件的ID值,可由Symbian SDK相关文档中找到。此模块的类视图见图 4.22。

图 4.22 重力加速度获取模块类图

用户界面

用户界面是用户与客户端进行交流的接口,在本解决方案中,采用了Model-View-Controller模型,整体构架如图4.23所示:

图 4.23 用户界面框架

采取这样的模式使视图层与业务层分离,有利于降低耦合性,增加了可维护性,加开了相关开发速度。用户界面相关的整体结构类图4.24所示:

图 4.24 用户界面类图

用户界面主要有以下功能:

  1. 接收用户的选择

用户界面首先需要实现的就是让用户选择连接的服务端这个功能,此功能的实现 直接通过上文所提到的蓝牙模块中的Bluetooth Device Selection UI进行,此过程会首先弹出一个对话框显示最近连接过的蓝牙设备供用户选择,如在此中没有用户想要选择的服务端,则可点击搜索附近的设备进行搜索,UI运 行时画面如图4.25所示:

图 4.25 选择连接服务端界面

  1. 在Joystick模式下响应用户按键

当客户端模式切换到Joystick模式时,客户端需要获取用户的按键信 息,并把这些信息发送给服务端,这时需要对按键进行监控,并同时将按键映射到其相对应的Joystick按键。当用户按下一个键 后,keyboard hardware就会生成一个中断,由keyboard driver捕捉,之后分解出这次按键事件的key code,然后 driver将它发送到系统端的一个线程——被称为window server,而window server又会把它发向在window group中 拥有焦点的那个应用程序中,这个步骤是使用一个control environment来完成的,这是window server和 user interface library之间的一个API函数。按键得映射是由CButtonMapper类完成,在获取到按键相关信息时其会转换 成相应所需按键信息并储存起来等待发送给服务端。在对用户的按键行为作出捕捉转换的同时,还需要将相应转换好的信息反馈给用户,以使用户知道服务端接收到 了什么信息,此过程通过UI上的变化执行,当接收到用户按键事件并把按键转换成相关Joystick信息后,通过UI上图片的切换让用户了解其所进行的操 作,此段的运行时画面如图4.26所示:

图 4.26 响应用户按键

  1. 在各种模式下接受服务端发送过来的反馈信息并传达给用户

用户界面不仅要能响应用户的操作,还应该将从服务端获取的分析结果也直观明 了的显示给用户,此过程首先由CBT类(bluetooth实现)接收到服务端的相关消息,然后对消息进行解析,分析出消息所对应的模式,以及消息的正体 内容,然后将解析的结果发送给程序UI进行显示。主要分成两种模式:

  1. Joystick模式下

在Joystick模式下,客户端将相应重力加速度信息传递给了服务端,服 务端对这些数据进行分析计算,将其模拟成Joystick的数值(如X,Y等),客户端需要的是这些数据所代表的方向信息,即这些数据表示的运动方向,服 务端会将此类信息回发至客户端,客户端在收到解析完消息后通过在UI上图片的切换让用户知道刚刚的操作所代表的方向信息,运行时画面如图4.27所示:

图 4.27 显示反馈的Joystick方向信息

  1. Gesture Control模式下

在Gesture Control模式,服务端对一连串加速度数据进行分析计算,通过相关数值特征判断用户正在用客户端进行操作的手势信息,然后会将识别出的手势以及手势所 对应的相关操作回发至客户端,客户端在收到解析完消息后,会在屏幕上显示出刚刚识别出的手势类型,以及执行的操作,运行时画面如图4.28所示:

图 4.28 显示反馈的Gesture既相对应命令

其他显示给用户的合理信息

  1. 供用户选择操作菜单

在程序运行的开始程序便会提示按照正常的顺序选择连接的服务端,并且选择工作的模式,不过有时用户需要改变模式,有时需要重新连接到其他设备,所以设计了这个选择操作菜单,当用户在任意时刻按下左功能软键(屏幕上有提示)

[转载]Flex拖动实现方法

mikel阅读(1038)

[转载]Flex拖动实现方法 – 镜涛的家 JT – 博客园.

在交互性要求较高的系统中,拖动是一种比较常用的技术,例如,我们经常用到权限定制、数据导入导出定制等功能,这种情况下,一般是目标数据 集合已经确定,用户需要从已有的集合中选择条目,使用拖动完成实现起来比较直观,友好;有些场景下需要允许容器内的元素能够自由拖动,例如一些图形设计工 具。在Flex中,它本身就提供了很多支持拖动的特性,很多情况下,这些特性能够简化我们的开发,但是也有一些情况是需要我们自定义实现的。本文介绍了三 种flex中进行拖动的方法。

方法一:List中数据的拖动

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute>

<mx:List dragEnabled=”true” width=”162” labelField=”Name>

<mx:Array>

<mx:Object Name=”Item a/>

<mx:Object Name=”Item b/>

<mx:Object Name=”Item c/>

</mx:Array>

</mx:List>

<mx:List dropEnabled=”true” x=”196” y=”0” width=”171” labelField=”Name>

</mx:List>

</mx:Application>

通过指定List的dragEnabled=true来允许List控件数据支持拖动,指定dropEnabled=true来允许List控件支持接受拖动数据。

方法二:容器中控件的拖动

这种方式下控件的拖动有两种方法,一种是利用Flex自身的实现,一种是使用自定义的实现。对于Flex内的所有可视控件,都支持startDrag()和stopDrag()方法,我们通过定制相应的事件即可完成拖动的操作。例如:

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute>

<mx:Script>

<![CDATA[

import mx.core.UIComponent;

private function start(e:MouseEvent):void

{

var component:UIComponent=e.currentTarget as UIComponent

component.startDrag();

}

private function stop(e:MouseEvent):void

{

var component:UIComponent=e.currentTarget as UIComponent

component.stopDrag();

}

]]>

</mx:Script>

<mx:Canvas x=”0” y=”0” width=”100%” height=”100%>

<mx:Button x=”237” y=”69” label=”Button” mouseDown=”start(event)” mouseUp=”stop(event)”/>

<mx:CheckBox x=”424” y=”49” label=”Checkbox” mouseDown=”start(event)” mouseUp=”stop(event)”/>

<mx:Image x=”344” y=”138” mouseDown=”start(event)” mouseUp=”stop(event)”/>

<mx:Label x=”267” y=”233” text=”Label” mouseDown=”start(event)” mouseUp=”stop(event)”/>

<mx:TextInput x=”412” y=”196” mouseDown=”start(event)” mouseUp=”stop(event)”/>

</mx:Canvas>

</mx:Application>

第二种方法就是自定义拖动的实现,主要的原理就是:在控件mouseDown之后,给所处的container注册mouseMove和 mouseUp事件及处理函数;mouseMove的时候计算鼠标移动的值,然后将控件坐标做相应的改变,mouseUp的时候移除mouseMove和 mouseUp处理函数。

代码如下:

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” xmlns:ns1=”flowdesigner.*” creationComplete=”init()”>

<mx:Script>

<![CDATA[

import flowdesigner.ActivityNode;

import ui.action.ObjectHandles;

private var mouseDownPosition:Point=null;

private function init():void

{

toDrag.addEventListener(MouseEvent.MOUSE_DOWN,componentMouseDownHandler);

}

public function componentMouseDownHandler(e:MouseEvent):void

{

container.stage.addEventListener(MouseEvent.MOUSE_MOVE,containerMouseMoveHandler);

container.stage.addEventListener(MouseEvent.MOUSE_UP,containerMouseUpHandler);

mouseDownPosition=new Point(e.stageX,e.stageY);

}

public function containerMouseMoveHandler(e:MouseEvent):void

{

var currentPoint:Point=container.globalToLocal(new Point(e.stageX,e.stageY));

var temp:Point=container.globalToLocal(mouseDownPosition);

var transation_x:int=currentPoint.x-temp.x;

var transation_y:int=currentPoint.y-temp.y;

toDrag.x=mouseDownPosition.x+transation_x;

toDrag.y=mouseDownPosition.y+transation_y;

}

public function containerMouseUpHandler(e:MouseEvent):void

{

container.stage.removeEventListener(MouseEvent.MOUSE_MOVE,containerMouseMoveHandler);

container.stage.removeEventListener(MouseEvent.MOUSE_UP,containerMouseUpHandler);

}

]]>

</mx:Script>

<ns1:DrawCanvas id=”container” width=”100%” height=”100%” backgroundColor=”#FFFDFD>

<mx:Canvas id=”toDrag” width=”121” height=”82” backgroundColor=”#BA8080/>

</ns1:DrawCanvas>

</mx:Application>

Init中给需要拖动的控件注册mouseDown处理函数;鼠标按下的时候给容器注册mouseMove和mouseUp处理函数,同时记录初始 坐标值;mouseMove的时候,将当前事件坐标值和原始坐标值分别转换为容器中相对坐标值,然后计算x、y坐标轴上的相对移动距离,最后设置控件坐标 值;mouseUp时取消相应的事件处理。

方法三:自定义拖动

这种方式一般能够满足大多数的情景,通过DragManager自己实现dragEnter,dragDrop事件来完成拖动功能。

DragManager 类管理拖放操作。当用户使用鼠标选择某个项目时,所选组件称为拖动启动器。拖动操作期间显示的图像称为拖动代理。当用户将拖动代理移动到其它组件时,系统 会向该组件发送 dragEnter 事件。如果该组件接受拖动,即可成为拖放目标,并接收 dragOver、 dragExit 和 dragDrop 事件。拖动操作完成后,会向拖动启动器发送 dragComplete 事件。DragSource 类中包含正被拖动的数据。

代码如下:

<?xml version=”1.0″ encoding=”utf-8″?>

<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute>

<mx:Script>

<![CDATA[

import mx.containers.Canvas;

import mx.core.DragSource;

import mx.managers.DragManager;

import mx.controls.Image;

import mx.events.DragEvent;

private function beginDrag(e:MouseEvent):void

{

var img:Image=e.currentTarget as Image;

var dragImg:Image=new Image();

dragImg.source=img.source;

var ds:DragSource=new DragSource();

ds.addData(img,“dragSource”);

DragManager.doDrag(img,ds,e,dragImg);

}

private function acceptDrag(e:DragEvent):void

{

var container:Canvas=e.currentTarget as Canvas;

DragManager.acceptDragDrop(container);

}

private function completeDrag(e:DragEvent):void

{

var dragTarget:Image=e.dragSource.dataForFormat(“dragSource”) as Image;

var img:Image=new Image();

img.source=dragTarget.source;

img.addEventListener(MouseEvent.MOUSE_DOWN,function(e:MouseEvent):void{Image(e.currentTarget).startDrag();});

img.addEventListener(MouseEvent.MOUSE_UP,function(e:MouseEvent):void{Image(e.currentTarget).stopDrag();});

var container:Canvas=e.currentTarget as Canvas;

container.addChild(img);

}

]]>

</mx:Script>

<mx:VBox x=”56” y=”61” width=”520” height=”435>

<mx:Canvas width=”100%” height=”40” x=”20” y=”50>

<mx:Image source=”assets/test.gif” mouseDown=”beginDrag(event)” width=”23” height=”19” x=”10” y=”11/>

</mx:Canvas>

<mx:Canvas width=”448” height=”358” dragEnter=”acceptDrag(event)” dragDrop=”completeDrag(event)” borderColor=”#39749D” borderStyle=”solid” backgroundColor=”#EDE5E5>

</mx:Canvas>

</mx:VBox>

</mx:Application>

首先,我们对需要拖动的对象增加mouseDown处理函数,本例中是对image对象进行拖动,mouseDown中首先获取当前事件对象,然后 创建一个副本,同时将该副本作为拖动源,通过DragManager开始拖动;对于接受拖动对象的容器dragEnter时通过DragManager.acceptDragDrop来指定该容器可接受拖动对象,最后在接受拖动对象容器的dragDrop事件处理函数中进行拖动完成的相关处理,本例中是向容器中增加图形。

[转载]使用 Model-View-ViewModel 的问题和解决方案

mikel阅读(1016)

[转载]使用 Model-View-ViewModel 的问题和解决方案 – 爱因斯坦的小脑 – 博客园.

先声明此文是转自MSDN杂志的。之前我和一些同行讨论过MVVM模式,很多人都觉得Silverlight或者WPF项目中没多大必要使用它。今天看到这篇神作,我觉得有必要给大家转过来看看。。。仔细阅读,相信你会受益匪浅。原文地址:http://msdn.microsoft.com/en-us/magazine/ff798279.aspx

关于作者:Robert McCarter 是加拿大的一位兼职软件开发人员、架构师和企业家。您可以在 robertmccarter.wordpress.com 阅读他的博客。

[转载]Asp.net MVC2 数据验证的扩展,结合Enterprise Library 将验证规则存入配置文件

mikel阅读(844)

[转载]Asp.net MVC2 数据验证的扩展,结合Enterprise Library 将验证规则存入配置文件。 – Harold Shen – 博客园.

上周四写了一篇自己对ASP.NET MVC 2 数据验证的理解,觉得MVC2本身的数据验证不是很理想,如下:

1、过多的属性(Attribute)使得Model或者Action参数过于臃肿。

2、Model的复用性不好,几乎是一个Action就要建立一个Model,譬如CreateAction和 UpdateCreateAction,它们的Model大部分是类似的,但是如果这两个Action对某几项属性的验证不统一,就会导致我们不得不创建 两个Model, 这样造成了冗余和可维护性变差。

3、对Action中简单类型的参数不能做更进一步的验证,因为他们不是自定义的Model, 所以ASP.NET Mvc2 对这种类型的参数几乎只做了类型的验证,没法做更进一步的验证。

4、对于Action中简单类型的参数,如果用户的输入不能对应于参数类型,那么可能会造成ASP.NET Mvc2的调用异常,譬如,Action中有一个简单类型的参数int age, Action1(int age),如果在form中用户的输入不是数字,这时会造成ASP.NET MVC2的调用异常

5、对于Model中的某个简单类型的属性,如果用户的输入不能对应于参数类型,此时Asp.net Mvc2会自动为我们设置一个此简单类型的默认值,譬如class Model1 { Int Age { get; set;} },如果在form 中用户的输入不是数字, 这时不会造成ASP.NET MVC2的调用异常,但是它会使用MVC本身的错误消息,而开发者本身却不会知道这种情况,这其实也是数据绑定部分的一个小问题,但是这也没法避免

6、对于Model中内嵌的复杂类型,ASP.NET MVC2不会去验证其内部的属性,譬如Class Model1 { Model2 { get;set; } } class Model2 { string P1 { get; set;} } 当验证Model1 时,Asp.net Mvc2不会去验证Model2内部P1的属性,这也意味着我们不能复用任何Model,这和问题一类似。

这些问题有很多的解决方案,在我的前一篇文章中,很多人对我提出的问题,大致都给了一些建议:

1、对于问题1 属性臃肿,有人建议使用Metadata。  对此,其实它还是要放很多的Attribute,只是放在了一个Partical类中。

2、对于问题2 Model的复用性,可能有些人都没理解我的意思,就让我去看 BO, VO, 呵呵,我真的很无奈,这里我根本就没有牵扯到什么业务逻辑,这里谈的一直

是Asp.net MVC, Asp.net MVC很多人都知道只是表现层的设计,所以这里的Model 指的是VO,当然 如果为了复用性,有些人将BO作为VO也不是不可以,因为在软件的架构中,Model是纵向的,它贯通了所有层,看下图。

http://www.google.com/imgres?imgurl=http://images1.itqun.net/2010325/201032520258_ps05.gif&imgrefurl=http://www.itqun.net/content-detail/182484_2.html&h=490&w=658&sz=148&tbnid=ins8HC_2m68DPM:&tbnh=103&tbnw=138&prev=/images%3Fq%3D%25E7%25B3%25BB%25E7%25BB%259F%2B%25E6%259E%25B6%25E6%259E%2584%25E5%259B%25BE&hl=en&usg=__aEZ6NTGAnd6pFkdhvL0W6y6mvNA=&sa=X&ei=CbRfTMiSEYeWvAP8rd2ZDA&ved=0CB0Q9QEwAQ

3、对于问题3,有人还是说要建立Model, 不然就不是MVC了,因为没有Model了,我想你说的办法能解决问题,但是为只有一个参数的 Action就建立一个Model,有点浪费啊,还有说不用Model 就不是 MVC了,就有点言过其实了,难道只有复杂类型是Model吗?一个string 类型就不是Model?

4、对于问题4其实它也和Asp.net MVC的数据绑定有关系,有人也给出了解决方案,就是JS在客户端验证,这个办法听起来不错,但实际不是解决办法,第一,我们要搞清楚客户端验证和服务器端验证的目的,客户端验证实际是为了帮助用户填写表单,提醒用户那些该输入,该怎么输,输入数字还是日期之类,它的目的是帮助,服务器端的验证才是真正的数据验证,如果一个系统能够相信客户端JS  的验证,那么这个系统存在的问题绝对是巨大的,大家想想,如果客户端可信,那么在Asp.net中,为什么微软还要加密它的ViewState?

5、对于问题5,我们后面会说。

6、对于问题6、有人说如果验证内嵌的属性,那就不是面向对象了,看到这里我也很无奈,在Asp.net MVC的数据绑定中,如果有内嵌的属性,这些属性都会被赋值的,为什么数据绑定的时候就没人说这样就不是面向对象了,而到了验证部分,就说不是面向对象了?

后面我们来谈谈如何进行扩展

Asp.netMVC的数据验证是在绑定之后被调用的,而且只会对复杂属性进行验证,它是使用Attribute,简单类型没设置Attribute, 大家如果看过源代码应该能在DefaultModelBinder中看到如下函数:

1 internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) {
2 // need to replace the property filter + model object and create an inner binding context
3 ModelBindingContext newBindingContext = CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model);
4
5 // validation
6 if (OnModelUpdating(controllerContext, newBindingContext)) {
7 BindProperties(controllerContext, newBindingContext);
8 OnModelUpdated(controllerContext, newBindingContext);
9 }
10 }

在这个函数中,调用了OnModelUpdated,这个方法,如果想验证可以Override这个方法做验证,但是还是只能验证Action 的复杂类型参数,不能验证Action的简单类型参数,于是我就用了其它办法,大家可以再去看看DefaultModelBinder是被那个类调用的, 应该在 ControllerActionInvoker 中 能找到下面这个代码:

1 protected virtual object GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) {
2 // collect all of the necessary binding properties
3 Type parameterType = parameterDescriptor.ParameterType;
4 IModelBinder binder = GetModelBinder(parameterDescriptor);
5 IValueProvider valueProvider = controllerContext.Controller.ValueProvider;
6 string parameterName = parameterDescriptor.BindingInfo.Prefix ?? parameterDescriptor.ParameterName;
7 Predicate<string> propertyFilter = GetPropertyFilter(parameterDescriptor);
8
9 // finally, call into the binder
10 ModelBindingContext bindingContext = new ModelBindingContext() {
11 FallbackToEmptyPrefix = (parameterDescriptor.BindingInfo.Prefix == null), // only fall back if prefix not specified
12 ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterType),
13 ModelName = parameterName,
14 ModelState = controllerContext.Controller.ViewData.ModelState,
15 PropertyFilter = propertyFilter,
16 ValueProvider = valueProvider
17 };
18
19 object result = binder.BindModel(controllerContext, bindingContext);
20 return result ?? parameterDescriptor.DefaultValue;
21 }

看到这里,我就想在调用完这个函数之后再对每个参数做验证,经过验证,发现这完全可行,于是我就继承了 ControllerActionInvoker,但是要想让Asp.net MVC2使用我的ControllerActionInvoker,还不得不去创建一个BaseController,这样才可以,讲到这里其实我的大概 思路已经出来了,后面就是将那些Enterprise Library  的验证规则放在配置文件中了,当然遇到的问题,却不止一点点,后面还有很多,因为时间的问题,我来不及写了。我会将源代码附上,供大家参考去写你们自己的 验证。

后面是我的验证的一些效果图:

首先是一个配置文件:

代码

1 <aspnet.mvc.validation>
2
3 <models>
4 <model name=”lm1″>
5 <properties>
6 <property name=”UserName”>
7 <validators>
8 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.NotNullValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
9 negated=”false” messageTemplate=”字段不能为空!” messageTemplateResourceName=””
10 messageTemplateResourceType=”” name=”Not Null Validator” />
11 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
12 culture=”zh-CN” lowerBound=”0″ lowerBoundType=”Inclusive”
13 upperBound=”10″ messageTemplate=”数值必须在0~10以内” name=”Range Validator” />
14 </validators>
15 </property>
16 <property name=”Password”>
17 <validators>
18 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.NotNullValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
19 negated=”false” messageTemplate=”字段不能为空!” messageTemplateResourceName=””
20 messageTemplateResourceType=”” name=”Not Null Validator” />
21 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
22 culture=”zh-CN” lowerBound=”0″ lowerBoundType=”Inclusive”
23 upperBound=”10″ messageTemplate=”数值必须在0~10以内” name=”Range Validator” />
24 </validators>
25 </property>
26 <property name=”RememberMe”>
27 <validators>
28 </validators>
29 </property>
30 <property name=”Embedded” ref=”em” />
31 </properties>
32 </model>
33
34 <model name=”em”>
35 <properties>
36 <property name=”TryCount”>
37 <validators>
38 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.NotNullValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
39 negated=”false” messageTemplate=”字段不能为空!” messageTemplateResourceName=””
40 messageTemplateResourceType=”” name=”Not Null Validator” />
41
42 <validator targetType=”System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″ negated=”false” messageTemplate=”类型不匹配” messageTemplateResourceName=””
43 messageTemplateResourceType=”” tag=”” type=”Harold.Net.Web.Mvc.Extension.MvcTypeConversionValidator, Harold.Net.Web.Mvc.Extension” name=”Type Conversion Validator” />
44
45 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
46 culture=”zh-CN” lowerBound=”0″ lowerBoundType=”Inclusive”
47 upperBound=”10″ messageTemplate=”数值必须在0~10以内” name=”Range Validator” />
48 </validators>
49 </property>
50 <property name=”NonModel”>
51 <validators>
52 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.NotNullValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
53 negated=”false” messageTemplate=”字段不能为空!” messageTemplateResourceName=””
54 messageTemplateResourceType=”” name=”Not Null Validator” />
55 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
56 culture=”zh-CN” lowerBound=”0″ lowerBoundType=”Inclusive”
57 upperBound=”10″ messageTemplate=”数值必须在0~10以内” name=”Range Validator” />
58 </validators>
59 </property>
60 </properties>
61 </model>
62
63 </models>
64
65 <controllers>
66 <controller name=”AccountController”>
67 <actions>
68
69 <action name=”LogOn”>
70 <params>
71 <param name=”model” ref=”lm1″ />
72 <param name=”simpleArg”>
73 <validators>
74 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.NotNullValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
75 negated=”false”
76 messageTemplate=”字段不能为空!”
77 messageTemplateResourceName=””
78 messageTemplateResourceType=””
79 name=”Not Null Validator” />
80
81 <validator targetType=”System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″ negated=”false” messageTemplate=”类型不匹配” messageTemplateResourceName=””
82 messageTemplateResourceType=”” tag=”” type=”Harold.Net.Web.Mvc.Extension.MvcTypeConversionValidator, Harold.Net.Web.Mvc.Extension” name=”Type Conversion Validator” />
83
84 <validator type=”Microsoft.Practices.EnterpriseLibrary.Validation.Validators.RangeValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=null”
85 culture=”zh-CN” lowerBound=”-1″ lowerBoundType=”Inclusive”
86 upperBound=”10″ messageTemplate=”数值必须在0~10以内” name=”Range Validator” />
87 </validators>
88 </param>
89 </params>
90 </action>
91
92 </actions>
93
94 </controller>
95 </controllers>
96
97 </aspnet.mvc.validation>

在 配置文件中,我 按照Controller -> Action -> Parameter(Reference) -> Property (Reference) -> Validators 来组织整个验证体系,这样就和我们的 Controller直接对应上了,并且,考虑到复用性,用户可以建立Model, 然后让参数去引用这个 Model。而且对简单类型的参数也能像复杂函数一样的验证了。

有人可能觉得配置文件很复杂,如果有时间我可以写一个配置器,因为Asp.net MVC  的Action函数是有规律的,很容易就能写出一个简单的配置器,我想大家应该都能做到。

下面是对  AccountController中的LogOnAction的 验证代码和效果图。

1 public ActionResult LogOn(LogOnModel model, int simpleArg, string returnUrl)
2 {
3 ….
4 }
5
6
7
8
9
10 [DisplayColumn(“UserName”, “Password”, true)]
11 public class LogOnModel
12 {
13 [DisplayName(“User name”)]
14 public string UserName { get; set; }
15
16 [DataType(DataType.Password)]
17 [DisplayName(“Password”)]
18 public string Password { get; set; }
19
20 [DisplayName(“Remember me?”)]
21 public bool RememberMe { get; set; }
22
23 public EmbeddedClass Embedded { get; set; }
24 }
25
26 public class EmbeddedClass
27 {
28 [DisplayName(“Try count”)]
29 public int TryCount { get; set; }
30
31 [DisplayName(“Non Model”)]
32 public string NonModel { get; set; }
33 }

效果图1:

效果图2:

还有最后谈一点对有些人的评论,评论可以,建议别人看某项技术也是好的,但是不要自以为是的让别人学学这个,学学哪个,那个口气让人听了很不舒服。抛砖引玉之文,希望活跃大家的想法。

源代码:http://files.cnblogs.com/harold/Harold.Net.Web.Mvc.Extension.zip