﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-tivan</title><link>http://www.cnblogs.com/tivan/</link><description /><language>zh-cn</language><lastBuildDate>Sat, 11 Oct 2008 03:14:47 GMT</lastBuildDate><pubDate>Sat, 11 Oct 2008 03:14:47 GMT</pubDate><ttl>60</ttl><item><title>《解剖PetShop》系列之一</title><link>http://www.cnblogs.com/tivan/archive/2008/09/18/1293755.html</link><dc:creator>tivan</dc:creator><author>tivan</author><pubDate>Thu, 18 Sep 2008 15:25:00 GMT</pubDate><guid>http://www.cnblogs.com/tivan/archive/2008/09/18/1293755.html</guid><wfw:comment>http://www.cnblogs.com/tivan/comments/1293755.html</wfw:comment><comments>http://www.cnblogs.com/tivan/archive/2008/09/18/1293755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/tivan/comments/commentRss/1293755.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/tivan/services/trackbacks/1293755.html</trackback:ping><description><![CDATA[<p><a href="http://www.cnblogs.com/wayfarer/archive/2006/04/14/375382.html">PetShop的系统架构设计</a> <p>《解剖PetShop》系列之一 <p>前言：PetShop是一个范例，微软用它来展示.Net企业系统开发的能力。业界有许多.Net与J2EE之争，许多数据是从微软的PetShop和Sun的PetStore而来。这种争论不可避免带有浓厚的商业色彩，对于我们开发人员而言，没有必要过多关注。然而PetShop随着版本的不断更新，至现在基于.Net 2.0的PetShop4.0为止，整个设计逐渐变得成熟而优雅，却又很多可以借鉴之处。PetShop是一个小型的项目，系统架构与代码都比较简单，却也凸现了许多颇有价值的设计与开发理念。本系列试图对PetShop作一个全方位的解剖，依据的代码是PetShop4.0，可以从链接<a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/bdasamppet4.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/bdasamppet4.asp</a>中获得。 <p>一、PetShop的系统架构设计 <p>在软件体系架构设计中，分层式结构是最常见，也是最重要的一种结构。微软推荐的分层式结构一般分为三层，从下至上分别为：数据访问层、业务逻辑层（又或成为领域层）、表示层，如图所示： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps01.gif" border="0"> <p>图一：三层的分层式结构 <p>数据访问层：有时候也称为是持久层，其功能主要是负责数据库的访问。简单的说法就是实现对数据表的Select，Insert，Update，Delete的操作。如果要加入ORM的元素，那么就会包括对象和数据表之间的mapping，以及对象实体的持久化。在PetShop的数据访问层中，并没有使用ORM，从而导致了代码量的增加，可以看作是整个设计实现中的一大败笔。 <p>业务逻辑层：是整个系统的核心，它与这个系统的业务（领域）有关。以PetShop为例，业务逻辑层的相关设计，均和网上宠物店特有的逻辑相关，例如查询宠物，下订单，添加宠物到购物车等等。如果涉及到数据库的访问，则调用数据访问层。 <p>表示层：是系统的UI部分，负责使用者与整个系统的交互。在这一层中，理想的状态是不应包括系统的业务逻辑。表示层中的逻辑代码，仅与界面元素有关。在PetShop中，是利用ASP.Net来设计的，因此包含了许多Web控件和相关逻辑。 <p>分层式结构究竟其优势何在？Martin Fowler在《Patterns of Enterprise Application Architecture》一书中给出了答案： <p>1、开发人员可以只关注整个结构中的其中某一层； <p>2、可以很容易的用新的实现来替换原有层次的实现； <p>3、可以降低层与层之间的依赖； <p>4、有利于标准化； <p>5、利于各层逻辑的复用。 <p>概括来说，分层式设计可以达至如下目的：分散关注、松散耦合、逻辑复用、标准定义。 <p>一个好的分层式结构，可以使得开发人员的分工更加明确。一旦定义好各层次之间的接口，负责不同逻辑设计的开发人员就可以分散关注，齐头并进。例如UI人员只需考虑用户界面的体验与操作，领域的设计人员可以仅关注业务逻辑的设计，而数据库设计人员也不必为繁琐的用户交互而头疼了。每个开发人员的任务得到了确认，开发进度就可以迅速的提高。 <p>松散耦合的好处是显而易见的。如果一个系统没有分层，那么各自的逻辑都紧紧纠缠在一起，彼此间相互依赖，谁都是不可替换的。一旦发生改变，则牵一发而动全身，对项目的影响极为严重。降低层与层间的依赖性，既可以良好地保证未来的可扩展，在复用性上也是优势明显。每个功能模块一旦定义好统一的接口，就可以被各个模块所调用，而不用为相同的功能进行重复地开发。 <p>进行好的分层式结构设计，标准也是必不可少的。只有在一定程度的标准化基础上，这个系统才是可扩展的，可替换的。而层与层之间的通信也必然保证了接口的标准化。 <p>“金无足赤，人无完人”，分层式结构也不可避免具有一些缺陷： <p>1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构，很多业务可以直接造访数据库，以此获取相应的数据，如今却必须通过中间层来完成。 <p>2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能，为保证其设计符合分层式结构，可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。 <p>前面提到，PetShop的表示层是用ASP.Net设计的，也就是说，它应是一个BS系统。在.Net中，标准的BS分层式结构如下图所示： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps02.gif" border="0"> <p>图二：.Net中标准的BS分层式结构 <p>随着PetShop版本的更新，其分层式结构也在不断的完善，例如PetShop2.0，就没有采用标准的三层式结构，如图三： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps03.gif" border="0"> <p>图三：PetShop 2.0的体系架构 <p>从图中我们可以看到，并没有明显的数据访问层设计。这样的设计虽然提高了数据访问的性能，但也同时导致了业务逻辑层与数据访问的职责混乱。一旦要求支持的数据库发生变化，或者需要修改数据访问的逻辑，由于没有清晰的分层，会导致项目作大的修改。而随着硬件系统性能的提高，以及充分利用缓存、异步处理等机制，分层式结构所带来的性能影响几乎可以忽略不计。 <p>PetShop3.0纠正了此前层次不明的问题，将数据访问逻辑作为单独的一层独立出来： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps04.gif" border="0"> <p>图四：PetShop 3.0的体系架构 <p>PetShop4.0基本上延续了3.0的结构，但在性能上作了一定的改进，引入了缓存和异步处理机制，同时又充分利用了ASP.Net 2.0的新功能MemberShip，因此PetShop4.0的系统架构图如下所示： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps05.gif" border="0"> <p>图五：PetShop 4.0的体系架构 <p>比较3.0和4.0的系统架构图，其核心的内容并没有发生变化。在数据访问层（DAL）中，仍然采用DAL Interface抽象出数据访问逻辑，并以DAL Factory作为数据访问层对象的工厂模块。对于DAL Interface而言，分别有支持MS-SQL的SQL Server DAL和支持Oracle的Oracle DAL具体实现。而Model模块则包含了数据实体对象。其详细的模块结构图如下所示： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps06.gif" border="0"> <p>图六：数据访问层的模块结构图 <p>可以看到，在数据访问层中，完全采用了“面向接口编程”思想。抽象出来的IDAL模块，脱离了与具体数据库的依赖，从而使得整个数据访问层利于数据库迁移。DALFactory模块专门管理DAL对象的创建，便于业务逻辑层访问。SQLServerDAL和OracleDAL模块均实现IDAL模块的接口，其中包含的逻辑就是对数据库的Select，Insert，Update和Delete操作。因为数据库类型的不同，对数据库的操作也有所不同，代码也会因此有所区别。 <p>此外，抽象出来的IDAL模块，除了解除了向下的依赖之外，对于其上的业务逻辑层，同样仅存在弱依赖关系，如下图所示： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps07.gif" border="0"> <p>图七：业务逻辑层的模块结构图 <p>图七中BLL是业务逻辑层的核心模块，它包含了整个系统的核心业务。在业务逻辑层中，不能直接访问数据库，而必须通过数据访问层。注意图中对数据访问业务的调用，是通过接口模块IDAL来完成的。既然与具体的数据访问逻辑无关，则层与层之间的关系就是松散耦合的。如果此时需要修改数据访问层的具体实现，只要不涉及到IDAL的接口定义，那么业务逻辑层就不会受到任何影响。毕竟，具体实现的SQLServerDAL和OracalDAL根本就与业务逻辑层没有半点关系。 <p>因为在PetShop 4.0中引入了异步处理机制。插入订单的策略可以分为同步和异步，两者的插入策略明显不同，但对于调用者而言，插入订单的接口是完全一样的，所以PetShop 4.0中设计了IBLLStrategy模块。虽然在IBLLStrategy模块中，仅仅是简单的IOrderStategy，但同时也给出了一个范例和信息，那就是在业务逻辑的处理中，如果存在业务操作的多样化，或者是今后可能的变化，均应利用抽象的原理。或者使用接口，或者使用抽象类，从而脱离对具体业务的依赖。不过在PetShop中，由于业务逻辑相对简单，这种思想体现得不够明显。也正因为此，PetShop将核心的业务逻辑都放到了一个模块BLL中，并没有将具体的实现和抽象严格的按照模块分开。所以表示层和业务逻辑层之间的调用关系，其耦合度相对较高： <p><img src="http://www.cnblogs.com/images/cnblogs_com/wayfarer/change/ps08.gif" border="0"> <p>图八：表示层的模块结构图 <p>在图五中，各个层次中还引入了辅助的模块，如数据访问层的Messaging模块，是为异步插入订单的功能提供，采用了MSMQ（Microsoft Messaging Queue）技术。而表示层的CacheDependency则提供缓存功能。这些特殊的模块，我会在此后的文章中详细介绍。</p><img src ="http://www.cnblogs.com/tivan/aggbug/1293755.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/42934/" target="_blank">[新闻]2008年10月11日科技博客精选</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>引用 Net中的反射使用入门</title><link>http://www.cnblogs.com/tivan/archive/2008/09/18/1293753.html</link><dc:creator>tivan</dc:creator><author>tivan</author><pubDate>Thu, 18 Sep 2008 15:23:00 GMT</pubDate><guid>http://www.cnblogs.com/tivan/archive/2008/09/18/1293753.html</guid><wfw:comment>http://www.cnblogs.com/tivan/comments/1293753.html</wfw:comment><comments>http://www.cnblogs.com/tivan/archive/2008/09/18/1293753.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/tivan/comments/commentRss/1293753.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/tivan/services/trackbacks/1293753.html</trackback:ping><description><![CDATA[<p>提纲： <p>1、 什么是反射 <p>2、 命名空间与装配件的关系 <p>3、 运行期得到类型信息有什么用 <p>4、 如何使用反射获取类型 <p>5、 如何根据类型来动态创建对象 <p>6、 如何获取方法以及动态调用方法 <p>7、 动态创建委托 <p>1、什么是反射 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Reflection，中文翻译为反射。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这是.Net中获取运行时类型信息的方式，.Net的应用程序由几个部分：‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成，而反射提供一种编程的方式，让程序员可以在程序运行期获得这几个组成部分的相关信息，例如： <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Assembly类可以获得正在运行的装配件信息，也可以动态的加载装配件，以及在装配件中查找类型信息，并创建该类型的实例。 <p>Type类可以获得对象的类型信息，此信息包含对象的所有要素：方法、构造器、属性等等，通过Type类可以得到这些要素的信息，并且调用之。 <p>MethodInfo包含方法的信息，通过这个类可以得到方法的名称、参数、返回值等，并且可以调用之。 <p>诸如此类，还有FieldInfo、EventInfo等等，这些类都包含在System.Reflection命名空间下。 <p>2、命名空间与装配件的关系 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很多人对这个概念可能还是很不清晰，对于合格的.Net程序员，有必要对这点进行澄清。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 命名空间类似与Java的包，但又不完全等同，因为Java的包必须按照目录结构来放置，命名空间则不需要。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 装配件是.Net应用程序执行的最小单位，编译出来的.dll、.exe都是装配件。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 装配件和命名空间的关系不是一一对应，也不互相包含，一个装配件里面可以有多个命名空间，一个命名空间也可以在多个装配件中存在，这样说可能有点模糊，举个例子： <p>装配件A： <p>namespace&nbsp;&nbsp; N1 <p>{ <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; AC1&nbsp;&nbsp; {…} <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; AC2&nbsp;&nbsp; {…} <p>} <p>namespace&nbsp;&nbsp; N2 <p>{ <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; AC3&nbsp;&nbsp; {…} <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; AC4{…} <p>} <p>装配件B： <p>namespace&nbsp;&nbsp; N1 <p>{ <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; BC1&nbsp;&nbsp; {…} <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; BC2&nbsp;&nbsp; {…} <p>} <p>namespace&nbsp;&nbsp; N2 <p>{ <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; BC3&nbsp;&nbsp; {…} <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; BC4{…} <p>} <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这两个装配件中都有N1和N2两个命名空间，而且各声明了两个类，这样是完全可以的，然后我们在一个应用程序中引用装配件A，那么在这个应用程序中，我们能看到N1下面的类为AC1和AC2，N2下面的类为AC3和AC4。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 接着我们去掉对A的引用，加上对B的引用，那么我们在这个应用程序下能看到的N1下面的类变成了BC1和BC2，N2下面也一样。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果我们同时引用这两个装配件，那么N1下面我们就能看到四个类：AC1、AC2、BC1和BC2。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到这里，我们可以清楚一个概念了，命名空间只是说明一个类型是那个族的，比如有人是汉族、有人是回族；而装配件表明一个类型住在哪里，比如有人住在北京、有人住在上海；那么北京有汉族人，也有回族人，上海有汉族人，也有回族人，这是不矛盾的。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面我们说了，装配件是一个类型居住的地方，那么在一个程序中要使用一个类，就必须告诉编译器这个类住在哪儿，编译器才能找到它，也就是说必须引用该装配件。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么如果在编写程序的时候，也许不确定这个类在哪里，仅仅只是知道它的名称，就不能使用了吗？答案是可以，这就是反射了，就是在程序运行的时候提供该类型的地址，而去找到它。 <p>有兴趣的话，接着往下看吧。 <p>3、运行期得到类型信息有什么用 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有人也许疑问，既然在开发时就能够写好代码，干嘛还放到运行期去做，不光繁琐，而且效率也受影响。 <p>这就是个见仁见智的问题了，就跟早绑定和晚绑定一样，应用到不同的场合。有的人反对晚绑定，理由是损耗效率，但是很多人在享受虚函数带来的好处的时侯还没有意识到他已经用上了晚绑定。这个问题说开去，不是三言两语能讲清楚的，所以就点到为止了。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我的看法是，晚绑定能够带来很多设计上的便利，合适的使用能够大大提高程序的复用性和灵活性，但是任何东西都有两面性，使用的时侯，需要再三衡量。 <p>接着说，运行期得到类型信息到底有什么用呢？ <p>还是举个例子来说明，很多软件开发者喜欢在自己的软件中留下一些接口，其他人可以编写一些插件来扩充软件的功能，比如我有一个媒体播放器，我希望以后可以很方便的扩展识别的格式，那么我声明一个接口： <p>public&nbsp;&nbsp; interface&nbsp;&nbsp; IMediaFormat <p>{ <p>string&nbsp;&nbsp; Extension&nbsp;&nbsp; {get;} <p>Decoder&nbsp;&nbsp; GetDecoder(); <p>} <p>这个接口中包含一个Extension属性，这个属性返回支持的扩展名，另一个方法返回一个解码器的对象（这里我假设了一个Decoder的类，这个类提供把文件流解码的功能，扩展插件可以派生之），通过解码器对象我就可以解释文件流。 <p>那么我规定所有的解码插件都必须派生一个解码器，并且实现这个接口，在GetDecoder方法中返回解码器对象，并且将其类型的名称配置到我的配置文件里面。 <p>这样的话，我就不需要在开发播放器的时侯知道将来扩展的格式的类型，只需要从配置文件中获取现在所有解码器的类型名称，而动态的创建媒体格式的对象，将其转换为IMediaFormat接口来使用。 <p>这就是一个反射的典型应用。 <p>4、如何使用反射获取类型 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先我们来看如何获得类型信息。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 获得类型信息有两种方法，一种是得到实例对象 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个时侯我仅仅是得到这个实例对象，得到的方式也许是一个object的引用，也许是一个接口的引用，但是我并不知道它的确切类型，我需要了解，那么就可以通过调用System.Object上声明的方法GetType来获取实例对象的类型对象，比如在某个方法内，我需要判断传递进来的参数是否实现了某个接口，如果实现了，则调用该接口的一个方法： <p>… <p>public&nbsp;&nbsp; void&nbsp;&nbsp; Process(&nbsp;&nbsp; object&nbsp;&nbsp; processObj&nbsp;&nbsp; ) <p>{ <p>Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; processsObj.GetType(); <p>if(&nbsp;&nbsp; t.GetInterface(“ITest”)&nbsp;&nbsp; !=null&nbsp;&nbsp; ) <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; … <p>} <p>… <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另外一种获取类型的方法是通过Type.GetType以及Assembly.GetType方法，如： <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; Type.GetType(“System.String”); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 需要注意的是，前面我们讲到了命名空间和装配件的关系，要查找一个类，必须指定它所在的装配件，或者在已经获得的Assembly实例上面调用GetType。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本装配件中类型可以只写类型名称，另一个例外是mscorlib.dll，这个装配件中声明的类型也可以省略装配件名称（.Net装配件编译的时候，默认都引用了mscorlib.dll，除非在编译的时候明确指定不引用它），比如： <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.String是在mscorlib.dll中声明的，上面的Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; Type.GetType(“System.String”)是正确的 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Data.DataTable是在System.Data.dll中声明的，那么： <p>Type.GetType(“System.Data.DataTable”)就只能得到空引用。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 必须： <p>Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,&nbsp;&nbsp; Culture=neutral,&nbsp;&nbsp; PublicKeyToken=b77a5c561934e089"); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这样才可以，大家可以看下面这个帖子： <p><a href="http://expert.csdn.net/Expert/topic/2210/2210762.xml?temp=.1919977">http://expert.csdn.net/Expert/topic/2210/2210762.xml?temp=.1919977</a> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qqchen的回答很精彩 <p>5、如何根据类型来动态创建对象 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Activator提供了方法来根据类型动态创建对象，比如创建一个DataTable： <p>Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,&nbsp;&nbsp; Culture=neutral,&nbsp;&nbsp; PublicKeyToken=b77a5c561934e089"); <p>DataTable&nbsp;&nbsp; table&nbsp;&nbsp; =&nbsp;&nbsp; (DataTable)Activator.CreateInstance(t); <p>&nbsp;&nbsp;&nbsp; 例二：根据有参数的构造器创建对象 <p>namespace&nbsp;&nbsp; TestSpace&nbsp;&nbsp; { <p>public&nbsp;&nbsp; class&nbsp;&nbsp; TestClass <p>{ <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp;&nbsp; string&nbsp;&nbsp; _value; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; TestClass(string&nbsp;&nbsp; value)&nbsp;&nbsp; { <p>_value=value; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>} <p>} <p>… <p>Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; Type.GetType(“TestSpace.TestClass”); <p>Object[]&nbsp;&nbsp; constructParms&nbsp;&nbsp; =&nbsp;&nbsp; new&nbsp;&nbsp; object[]&nbsp;&nbsp; {“hello”};&nbsp;&nbsp; //构造器参数 <p>TestClass&nbsp;&nbsp; obj&nbsp;&nbsp; =&nbsp;&nbsp; (TestClass)Activator.CreateInstance(t,constructParms); <p>… <p>把参数按照顺序放入一个Object数组中即可 <p>6、如何获取方法以及动态调用方法 <p>namespace&nbsp;&nbsp; TestSpace <p>{ <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; TestClass&nbsp;&nbsp; { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private&nbsp;&nbsp; string&nbsp;&nbsp; _value; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; TestClass()&nbsp;&nbsp; { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; TestClass(string&nbsp;&nbsp; value)&nbsp;&nbsp; { <p>_value&nbsp;&nbsp; =&nbsp;&nbsp; value; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; string&nbsp;&nbsp; GetValue(&nbsp;&nbsp; string&nbsp;&nbsp; prefix&nbsp;&nbsp; )&nbsp;&nbsp; { <p>if(&nbsp;&nbsp; _value==null&nbsp;&nbsp; ) <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; "NULL"; <p>else <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; prefix+"&nbsp;&nbsp; :&nbsp;&nbsp; "+_value; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; string&nbsp;&nbsp; Value&nbsp;&nbsp; { <p>set&nbsp;&nbsp; { <p>_value=value; <p>} <p>get&nbsp;&nbsp; { <p>if(&nbsp;&nbsp; _value==null&nbsp;&nbsp; ) <p>return&nbsp;&nbsp; "NULL"; <p>else <p>return&nbsp;&nbsp; _value; <p>} <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>} <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 上面是一个简单的类，包含一个有参数的构造器，一个GetValue的方法，一个Value属性，我们可以通过方法的名称来得到方法并且调用之，如： <p>//获取类型信息 <p>Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; Type.GetType("TestSpace.TestClass"); <p>//构造器的参数 <p>object[]&nbsp;&nbsp; constuctParms&nbsp;&nbsp; =&nbsp;&nbsp; new&nbsp;&nbsp; object[]{"timmy"}; <p>//根据类型创建对象 <p>object&nbsp;&nbsp; dObj&nbsp;&nbsp; =&nbsp;&nbsp; Activator.CreateInstance(t,constuctParms); <p>//获取方法的信息 <p>MethodInfo&nbsp;&nbsp; method&nbsp;&nbsp; =&nbsp;&nbsp; t.GetMethod("GetValue"); <p>//调用方法的一些标志位，这里的含义是Public并且是实例方法，这也是默认的值 <p>BindingFlags&nbsp;&nbsp; flag&nbsp;&nbsp; =&nbsp;&nbsp; BindingFlags.Public&nbsp;&nbsp; |&nbsp;&nbsp; BindingFlags.Instance; <p>//GetValue方法的参数 <p>object[]&nbsp;&nbsp; parameters&nbsp;&nbsp; =&nbsp;&nbsp; new&nbsp;&nbsp; object[]{"Hello"}; <p>//调用方法，用一个object接收返回值 <p>object&nbsp;&nbsp; returnValue&nbsp;&nbsp; =&nbsp;&nbsp; method.Invoke(dObj,flag,Type.DefaultBinder,parameters,null); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 属性与方法的调用大同小异，大家也可以参考MSDN <p>7、动态创建委托 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 委托是C#中实现事件的基础，有时候不可避免的要动态的创建委托，实际上委托也是一种类型：System.Delegate，所有的委托都是从这个类派生的 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Delegate提供了一些静态方法来动态创建一个委托，比如一个委托： <p>namespace&nbsp;&nbsp; TestSpace&nbsp;&nbsp; { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delegate&nbsp;&nbsp; string&nbsp;&nbsp; TestDelegate(string&nbsp;&nbsp; value); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; class&nbsp;&nbsp; TestClass&nbsp;&nbsp; { <p>public&nbsp;&nbsp; TestClass()&nbsp;&nbsp; { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; void&nbsp;&nbsp; GetValue(string&nbsp;&nbsp; value)&nbsp;&nbsp; { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return&nbsp;&nbsp; value; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>} <p>使用示例： <p>TestClass&nbsp;&nbsp; obj&nbsp;&nbsp; =&nbsp;&nbsp; new&nbsp;&nbsp; TestClass(); <p>//获取类型，实际上这里也可以直接用typeof来获取类型 <p>Type&nbsp;&nbsp; t&nbsp;&nbsp; =&nbsp;&nbsp; Type.GetType(“TestSpace.TestClass”); <p>//创建代理，传入类型、创建代理的对象以及方法名称 <p>TestDelegate&nbsp;&nbsp; method&nbsp;&nbsp; =&nbsp;&nbsp; (TestDelegate)Delegate.CreateDelegate(t,obj,”GetValue”); <p>String&nbsp;&nbsp; returnValue&nbsp;&nbsp; =&nbsp;&nbsp; method(“hello”); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 到这里，我们简单的讲述了反射的作用以及一些基本的用法，还有很多方面没有涉及到，有兴趣的朋友可以参考MSDN。 <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很奇怪，很多人都不愿看MSDN，其实你想要的答案，99％都可以在里面找到</p><img src ="http://www.cnblogs.com/tivan/aggbug/1293753.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/42934/" target="_blank">[新闻]2008年10月11日科技博客精选</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>深入理解SOA和Enterprise Web 2.0</title><link>http://www.cnblogs.com/tivan/archive/2008/09/18/1293750.html</link><dc:creator>tivan</dc:creator><author>tivan</author><pubDate>Thu, 18 Sep 2008 15:19:00 GMT</pubDate><guid>http://www.cnblogs.com/tivan/archive/2008/09/18/1293750.html</guid><wfw:comment>http://www.cnblogs.com/tivan/comments/1293750.html</wfw:comment><comments>http://www.cnblogs.com/tivan/archive/2008/09/18/1293750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/tivan/comments/commentRss/1293750.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/tivan/services/trackbacks/1293750.html</trackback:ping><description><![CDATA[<p> 请问你是怎么理解SOA的？ <a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-collapse.gif" border="0"></a> <p>我这个理解来说下来就是，有这么大概几个过程，也分成这么几个阶段。最早就是03年左右，我刚到CSDN的时候，那个时候呢，SOA有一段的这个宣传的这种高潮，各大公司，包括微软、IBM、Oracle、BEA啊，都在打SOA，所以当时我们作为技术媒体，不得不了解一下SOA是什么东西，在那个时候，我们是很模糊的。所以我的第一个阶段呢，我就认为在技术上，我认为SOA是一个用Web Services来做企业应用的，这么一个想法，所以本质上我只是觉得这东西没什么大不了得，因为在03年的时候，Web Services 这个概念已经火了两三年了，所以我觉得，SOA没有什么了不起的。 之后不久呢，通过跟企业接触，企业告诉我们呢，也是一个误解，现在来看也就是一个误解，就是SOA是一个新一代的企业应用集成这样的一种架构，也就是EAI的下一代，当时我们就这么看的一个问题。有人提出来说，中国是不是不太适合SOA，因为在中国EAI在企业没有特别多成熟的运用系统，主要依靠新建，所以呢，当时也比较困惑这个问题。后来SOA呢，进入了一段相对不是那么热闹的一个阶段，可以说进入了一个具体实施的一个时期，各家的认识和经验都在积累，我们的思想也处在一个相对来说比较稳定的一个状态。那么再过一段时间，当这个一些实施成果上来以后，包括我们去采访一些SOA实施，有了实践经验以后的这些人，我们作为媒体来讲，得到一个新的认识，就是因为SOA是一种新的企业的IT基础架构的，这么一种风格，一种架构，其中流行的一种东西就是一个ESB，即总线，然后很多Services通过根据总线相连接，简化之间的交互，达到松耦合等等。所以当时，我们那个时候，大概一年以前的时候，我对这件事情的认识呢，进化到它是一个风格，它是一种架构，它架构也可以具体实例化成一种技术，一个产品，这是我当时的一个认识。 那么到现在，到今天，又经过一段时间的演化，又经过了跟一些企业的交流，跟一些这个行里的大师，或者行业里的领导人物的交流，我最后，我觉得我现在的认识，相对可能比较接近1996年Gartner提出来SOA的概念，这SOA是指什么呢，SOA是一种新的建构企业应用和企业IT基础架构，主要是企业IT基础架构的思想、方法、风格、工具的一个总称，所以SOA现在的这个思想，比较泛，就是说比较宽广，就是说含义比较宽广。甚至我发明了一个顺口溜，说“SOA是个筐，什么都可以往里装”，只要你这些新方法，是符合企业IT基指架构发展的下一个潮流，一个阶段，那么现在很多人都把它会往SOA里去装，但是呢，SOA还是有它确切的含义和它明确的价值，所以，我刚才说的这个含义呢，其实是我现在的一个认识。  <p><a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-right.gif" border="0"></a> 我也知道你在前段时间去了美国，去参加一个IBM组织的SOA大会，然后也接触了很多国外的SOA专家，我想问一下，国外的那个技术人员对SOA的理解和国内的人对SOA的理解，有什么区别吗？ <a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-collapse.gif" border="0"></a> <p>恩，说实话，在这一点上，我不能说我知道的很清楚。我是跟国外的技术人员就这方面交流的并不多，但是呢，从国外的一些文章，网站上的一些文章，还从有限的一些交流来看的话，SOA这个概念即使在美国，这仍然是一个引起很多争议，引起很多的误会的这个概念。尤其是在开发者，在很技术的人员当中，SOA是一个招人讨厌的，或者说是不被人理解的，或者是别人觉得，你就是个叫Hip，这么一个东西。也有很多不同的认识跟理解，可是我觉得，如果要是正本溯源的话，还是应该去向Gartner这个提出SOA的这个组织，像IBM，像BEA，Oracle，包括微软，他们技术的决策者，技术方向的决策者，到那里去SOA的真经，如果我们到那里去问的话，我觉得，他们那里的SOA的思路跟我们国内目前一般开发人员，一般技术管理者的认识水平，那还是不太一样的，就是还是有，他们还是比我们要领先很多的。  <p><a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-right.gif" border="0"></a> 但是另外一种观点就是说，SOA是从国外开始兴起的，主要是去解决一些，去整合遗留的系统，但是我们国内的IT历史并不是特别长，你认为SOA对我们国内的IT界有什么作用？ <a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-collapse.gif" border="0"></a> <p>我需要指出的是，你刚才的那个Statement就不对，首先它是从国外来的，这是对的，我们所有的东西差不多都是从国外来的。但是说它只是为了整合遗留系统，这句话就有问题。因为SOA要做的第一件事情不是整合，第一件事情，当然还是重新梳理你的业务流程，把它SOA把它Service化，但是呢，在着手进行改造，第一件事情，打散你的业务，先破再立，不是简单的把过去的业务进行整合。我们如果运气好，你过去的业务系统呢，正好能被完整的包装成一个服务，那当然好了，但是如果你过去的业务系统，不好被完整的包装成一个服务，那就需要先把它打开，给张三的给张三，给李四的给李四，把它包装配成一个服务，这个是要纠正的。所以就是说，即使从国外的企业来讲，它也不纯粹是一个整合的概念，它确实是一个以服务为导向，重新规划系统的这么一个过程，而对中国呢，我讲过了刚才的观点，如果说国外呢，是改建为主，就是我的房子已经盖好了，我要改建，我一定要把它该拆的拆，该破的破，该打通的打通，对吧，但是它毕竟都属于改建；中国的情况呢，则是新建、改建、扩建并行，我们大量的系统，大量的企业，到现在还在怀疑IT的价值，它没有进行第一代的IT化的建设，还有一些企业已经进行了第一代的IT建设，还有很多的企业正在进行第二代的，第三代的IT建设。 从2003年开始，中国IT建设70%的投资是二次建设，所以你可以想像一下，做IT的公司才多少？大量的公司，大量的企业还没有做IT，还没有认真的把IT放到自己的这个管理中间来，包括还有一些非企业性质的，政府、学校和一些社会组织，都还没有把IT纳入自己的业务管理的过程当中，所以潜力是很大的。对于它们来说，一个主要的考虑就是，说白了，领导说话算不算话，这个很重要，领导说话要算话你就必须要让IT系统足够灵活。我们中国的企业是有这样的特点，和我们国家国情比较接近，我们喜欢突变，我们可能会在某一个水平线上，某一个状态上维持一个比较长的时间，然后突然一下发生巨大变化，这个时候就是要求我们IT系统非常灵活，其实我个人认为，我们中国的企业在国际市场上获得一些成果一个很重要的原因，我们以外国人想象不到的速度在改变自己。所以呢，这是我刚才上一个问题讲到的，中国的企业绝不会允许自己损失机动性，损失敏锐性，因为这是我们赖以生存的一个根本，所以如果你上一个IT系统，让我把这个敏锐性给损失掉了，我就不会上，也不会要的。 反映在很多现实当中，开发人员给我们无耐地摇头解释说，中国没法做IT，那领导人拍脑袋就变，你还非得听他的不可。这就说明一个问题，领导人拍脑袋就变，这可不一定是坏事，这可能是这个企业竞争力的源泉之一。所以我们在中国做企业的话，要支持这种东西，要足够灵活，要让IT系统能够跟随决策者的思想变化而变化。IT的意义本身在于使你的管理规范化，流程化，效率提高，严格，这样一个避免人为因素，降低对人力素质的要求，这样的一个特点。所以我们，还是刚才说的，我们中国的IT，目前在SOA要解决的问题就是不损失灵活性的情况下，提高管理水平、有效性、管理的效果。  <p><a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-right.gif" border="0"></a> 那么在实施SOA的过程中你认为应该注意哪些问题，有哪些难点存在？ <a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-collapse.gif" border="0"></a> <p>在实施SOA的过程当中，第一个难点，我觉得是要改变一些思维，改变一些传统的思维方式。传统的思维方式是把这种，从业务到IT系统，这种运作过程看成是一种魔术的，要靠培养巨牛无比的人，这个既要具有极强的面向对象这些基础的软件和IT基础的这种知识和技能，通过多年的磨炼，能够规避各种各样的风险；然后还要懂这个业务，要善于沟通，要用有效的方法来帮它及时地找到和发现自己的错误，这是非常难做到的事情。那么首先要改变观念就是说，我们现在一个层面是业务，一个层面是技术，所以我们在中间加一个层面，叫Service，这个Service是一个贴近技术，贴近业务，又可以被技术支持的一个概念，能够以它为标准，又能够以它为单位，对于业务来进行模块化，实现Service之后，你会发现，当你的业务要发生变化的时候，你所要做的仅仅是微调Service内部的接口、契约、服务质量和重新调整各个Service之间的关系，几乎就可以满足你对业务调整的需要。 这就是说我们把业务给模块化，找到合理的一个单元，可以用它来把业务这个事情模块化，而这个单元又特别巧的，也不是特别巧的，刻意地可以被技术，用我们现在传统的这一代组件啊，对象啊这些东西来支持和实现，这不是很好的事情吗？我们说一句谚语叫“当你在软件里面遇到任何问题的时候，请增加一个中间层”，这个就是这个思想的一个应用。增加了一个Service这样的一个中间层，一个间接层，来协调这两个差的太远的之间的关系，来做到这件事情。所以首先思想上要改变，要知道SOA为什么？知道了这一点，你才能够有可能去正确的去实施SOA，SOA在实施的时候第一步，应该是梳理业务流程；第二步，应该是用Service的观点重新描述你的业务流程，这个过程也是挺关键的，我认为，从这个过程往下，就比较容易一些，包括做一些具体实现Service的一些方法、操作和我们常用的一些方法。当然可能还有一些新的标准，SCA啊，SDO啊，包括ESB啊，包括一些提供的工具。 但总的来说，到了我们开发者日常工作这一块，他的改变有，但是他的工作形态的变化并不是很大，可是在这个业务梳理和业务架构这个过程当中，是有一个关键变化，这个是我觉得这个，你刚才说到难点在哪。那么这个难点归根到底就在于，我认为SOA存在的关键在于，它是否能够培养出足够多的高素质的，高质量的这种SOA架构师。SOA的架构师跟我们传统意义上的架构师不太一样，传统意义上的架构师就是变魔术的那帮人，也不知道他怎么想的就变了。现在呢，要把这个过程分成两步，第一步把它分解成为服务，把服务之间的关系理顺；第二步，把每个服务的接口、契约，全部都封装好，服务之间的关系，包括它的Governance之间的关系，这个叫做业务架构，这个人我认为是在SOA时代最吃香而且至关重要的一个脚色。  <p><a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-right.gif" border="0"></a> 你按照你的意思来说，就是我们现在的开发人员，或者说是一些架构师，做转变的话，要重视业务的发展，是这样吗？ <a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-collapse.gif" border="0"></a> <p>对，我觉得这句话其实说的很多。就是没有SOA时候，大家也都在做，你作为一个开发者也好，作为一个架构师也好，你一定要关注业务。但是呢，过去这种说法，是种泛泛的，我们不知道怎么关注，你总不能让我去成为一个石油专家，一个银行家，或者一个什么，才能够就是说开始这样的一个系统，那这样的人有多少呢？成为那样的人，还做IT系统吗？有这样的问题存在。现在呢，有一个具体的指导方针，就是说你该怎么去了解业务了，你要把业务分解成为一个一个的Service，理顺这些Service的关系，调整Service的接口和契约，这是对这个的理解。对开发者的理解，对开发者的影响来讲，有些实践就要变了。比如说，我们现在用到的一些，它已经熟习的一些思想、方法，一些东西可能有一些变化。 因为过去呢，你直接面向业务，开发者往往要学会一种能力，看了一段自然语言文本，就把一个什么对象图啊，什么模块图啊，类图啊等都画出来，这个是过去要求的一个。现在呢，你要面对的问题，可能就是说，你要面临的是怎么样最好的，向外界提供高质量的符合企业的服务，这是开发者的一个变化。但是更大的影响呢，对于开发者来讲的话，是他需要重新安排自己的职业生涯和规划，过去他觉得我学习这些东西也就可以成为架构师，设计师架构师怎么怎么样，或者我就专心的做我的.NET，做我的Java的内核，高效率的东西，也可以成为那个领域的专家，但是你要知道这是两个不同的路线。现在呢，你想做企业里面的架构师，有来之后，你就可能，对自己职业的规划，一个目标，要有一些调整和变化。  <p><a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-right.gif" border="0"></a> 我在看很多关于SOA的文章都提到各式各样的标准，我想问一下，为什么SOA里面会有那么多的标准？ <a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-collapse.gif" border="0"></a> <p>其实坦率的说，现在SOA一个很大的问题就是标准化不够，标准没有得到很好的制定和实施，甚至可以说，我可以这么讲，SOA，现在是有一个OSOA组织了，但是这个组织不是一个很强有力的组织，国际化的这个标准组织一般都是比较松散的，这个OSOA也是松散的。然后呢，各大厂商都有自己的想法和主意，结果就是说，SOA标准化的东西目前做的不是特别好，但是尽管有这么多的问题，还是有SCA、SDO这样的标准出现，这个对于SOA还是很好的事情，还有一些其他的标准，比如说WS-*，有三十多个标准，那些东西不属于SOA，尽管可以用它很好的来实现SOA，但是你不要把它作为SOA标准，所以在我看来呢，SOA的问题不是说标准化太多，而是标准不足的问题。我们马上面临很大的一个问题就是说，微软它做了一套体系，跟SOA的SCA、SDO标准是不一致的，这里的问题就是说，我们当然还要看，微软的WPF、WCF跟这个是相关的，以及WF这个标准，能够互相兼容，互相操作。如果可以的话，当然很好，如果不可以的话，怎么办？我们得到了一些微软方面的承诺，但是毕竟这个还是不官方承诺，我们想看看这个事情怎么发展。所以标准有，如果谈到SOA的话，标准真的不是太多了，而是还不够。  <p><a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-right.gif" border="0"></a> 另外然后业界里面流行的概念，一个是SOA，另外一个是Web 2.0，两者之间也是有很大的关系，我想请你评价一下这两者之间的关系？ <a href="http://www.infoq.com/cn/interviews/mengyan-soa-enterprise-web2#"><img src="http://www.infoq.com/styles/cn/i/icon-collapse.gif" border="0"></a> <p>这次我去美国的时候呢，我去参加的IBM Impact 2007这个大会，这个大会的主办者或者说最主要的一个人，是IBM软件集团的一个副总载，叫Sandy Carter，是一位女士，IBM的一位传奇的一位女性。她写了一本书，叫做《业务的新语言》，子标题是SOA&amp;Web2.0，所以从这句话来看，就知道这两个是有关系的。什么关系？我可以这么说的，这两个东西协同起来，可以共同为我们搭配刚才所说的敏捷业务的目标，为什么这么说，因为传统上，这个企业呀，你像我们业务系统总得用数据，业务逻辑，在它上面展现，还有一些整合的过程，有人提出Portal，企业级Portal这样一个概念，这么一些过程的话。实际上，当整个技术转移到外围以后，由于Web技术，第一代Web技术的制约，也使得它们在企业运行当中的，企业当中的一些运用受到了一些限制，现在的问题就是说，Web 2.0这东西怎么理解呀，大家还在说。我觉得固然有很多，从网络社会学意义角度去出发，去阐述Web 2.0，著名的博客Keso，他提了很多，比如说用户与这个编辑处于同等地位呀，强调自由啊，强调话语权的平等呀，开放性呀，强调用户权益至上，把这些东西列为Web 2.0的一个标杆。 但是从我们做IT的技术来讲，Web 2.0他有他固定的技术含义，他是指一系列的第二代的Web服务的集合，这里面包括像RSS、Atom，然后有这个Syndication，其实就是刚才说的这两个协议的抽象概念。然后有像这个Blog的这种服务，社会网络，技术上的有Ajax，数据的Mashup ，有数据的微格式，这样的一些东西，这个东西综合起来，能够给用户更好的体验，能够给实施者提供更轻便，更容易的一种集成，这种东西叫Web 2.0。那么它为什么可以用来和SOA一起来为企业来服务来使用呢？你记住，我们刚才说为了业务敏捷性，业务敏捷性就意味着，我们不仅仅数据和业务的关系发生变化，它给用户的展现，它的集成方式，它的集成范围，都会迅速发生改变，而在这一点上，传统的企业使用的C/S模型，或者第一代Web技术，它那种局限性，都使你没有办法很快地，使你的最后传到用户那里的最后的一公里也跟着一块变。 只有我们采用Web 2.0这些功能，Web 2.0提供的这些服务和协议，它这种风格，REST和Micro-format也就是微格式，这些东西融合起来，你才能够从下到上为用户，为企业提供一套完整的敏捷的技术栈，才能够说，人家的Idea一变，业务一变，底下的数据变，上面的模型，上面的业务叫流程变、服务变，流程变，最后到上头来，到前边Portal也变了，这个界面也变了，数据表单也变了，只有这个才能做到这一点。所以Sandy Carter在这本书里面画了一张图，我刚刚提到她也是这个原因，这张图是一个金字塔，然后她用一张薄片，把金字塔拦腰一切，底下SOA，上边Web 2.0，很明显地说明这个问题，就是说在底下要用企业级的SOA技术解决问题，将数据用标准方式如XML等暴露出来，上边用Web 2.0的技术去整合Mashup，去呈现，这种东西就会导致，就会使得我们刚才说的业务敏捷性的目标得以实现，这就是这两个技术应用之间的关系。 它们之间有什么关系？它们之间是盟友的关系，我们共同协作可以达成敏捷性的目标，否则的话，如果你几家是很快变化的数据和流程，上面我还是传统老旧的那一套，比如说Web 1.0的，或者是，甚至是更传统的VB或者是更传统的.NET富客户端所写的程序，当然对于企业最后还是得不到迅速变化，随之实施的这么一个结果。那我可以给你举一个小例子，这个Web 2.0一旦跟企业应用相结合，形成一个Enterprise Web 2.0这么一个新概念。 那么这个，我给你举一个小例子，已经实施的例子：就是有一家远洋航运公司，他们呢，做了这么样一个应用，就是他们通过GPS系统，将自己的每一艘航船在远洋，在地球上的位置，都能够实时的确定下来，他们把这个数据得到并放在一个服务器系统里面；他们另外一个系统保存着每一艘远洋航船此次航运他的目的地、起点、他的运货是哪些，这些定单信息，这些商务上的信息；另外一个系统保存着每艘航船里边的人都是谁，雇员的信息，海员的信息，临时工是谁，他的情况是什么样子。然后，其实现在就表示他们已经提供了一些服务了，在把这些服务跟就是，他把这些数据用SOA的方式，把这些服务给整合起来。整合起来之后，跟Web 2.0这个技术能够理解的格式比如XML以后，再跟Google Map整合，立刻得到了一张全球海洋图，一张大的一个全球地图，每一艘现在在什么位置都可以看到。当他点击那个船的时候，他可以立刻看到这个船本身的信息，船上船员的信息，这次航行的有关商务信息，都可以看到，这个就是SOA跟Web 2.0融合的一个例子。但故事还没完，当我们部署到内部的时候，好多事情就会不断的出现。因为你不仅可以缝合你，Mashup你自己企业内部的信息，当你采用公共的Enterprise Web 2.0作为你的架构以后，你就可以公开的自由的获得，或者是通过付费的方式，获得别的企业高质量的数据服务。他们很快又购买了一家海洋天气预报公司所提供的高质量的数据服务，然后Mashup到这张图里面，就可以看到天气的情况。然后更有意思的是，他们后来又想知道海盗报警，哪些地方发生海盗的事情，英国有家公司提供这样的信息，于是他们就把那些信息又通过Web Service的方式抓过来，那些公司的信息通过Web Service的方式暴露出来了，他们把这些信息抓过来，缝合到这张图里面去，让它来显示不同的信息。它立刻就可以得到，我这次航行处在什么样的气象条件下，海洋气象条件下，然后它前方或者它路过的地方，有没有海盗的这个情况，高危险地区我就要提示它警告它，这样的一个应用，我认为是目前我所知道的，比较好的Enterprise Web 2.0和SOA的应用，这么一个表现。我相信，由于这样的一个系统，据我所知，它是在一家全球顶级的大公司支持之下，来完成的。据说这个整个的项目的实施，尤其是后面融合数据的过程，都是非常快，花费统一，而且就是说，完全没有那些繁冗的过程，这就是Enterprise web 2.0的威力</p><img src ="http://www.cnblogs.com/tivan/aggbug/1293750.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/42934/" target="_blank">[新闻]2008年10月11日科技博客精选</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>javascripot div登陆提示框(页面层不可用)(转)</title><link>http://www.cnblogs.com/tivan/archive/2008/09/18/1293747.html</link><dc:creator>tivan</dc:creator><author>tivan</author><pubDate>Thu, 18 Sep 2008 15:15:00 GMT</pubDate><guid>http://www.cnblogs.com/tivan/archive/2008/09/18/1293747.html</guid><wfw:comment>http://www.cnblogs.com/tivan/comments/1293747.html</wfw:comment><comments>http://www.cnblogs.com/tivan/archive/2008/09/18/1293747.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/tivan/comments/commentRss/1293747.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/tivan/services/trackbacks/1293747.html</trackback:ping><description><![CDATA[<p>转自:http://blog.csdn.net/chinmo/archive/2008/04/20/2308911.aspx <p>&lt;html&gt;<br>&lt;head&gt;<br>&lt;meta http-equiv="Content-Type" content="text/html; charset=gb2312" /&gt;<br>&lt;title&gt;greencss.com&lt;/title&gt;<br>&lt;script type="text/javascript"&gt;<br>function sAlert(str,stitle){<br>var msgw,msgh,bordercolor;<br>msgw=300;//提示窗口的宽度<br>msgh=100;//提示窗口的高度<br>titleheight=25 //提示窗口标题高度<br>bordercolor="#336699";//提示窗口的边框颜色<br>titlecolor="#99CCFF";//提示窗口的标题颜色<br>var sWidth,sHeight;<br>sWidth=document.body.offsetWidth;//浏览器工作区域内页面宽度<br>sHeight=screen.height;//屏幕高度（垂直分辨率）<br>//背景层（大小与窗口有效区域相同，即当弹出对话框时，背景显示为放射状透明灰色）<br>var bgObj=document.createElement("div");//创建一个div对象（背景层）<br>//定义div属性，即相当于<br>//&lt;div id="bgDiv" style="position:absolute; top:0; background-color:#777; filter:progid:DXImagesTransform.Microsoft.Alpha(style=3,opacity=25,finishOpacity=75); opacity:0.6; left:0; width:918px; height:768px; z-index:10000;"&gt;&lt;/div&gt;<br>bgObj.setAttribute('id','bgDiv');<br>bgObj.style.position="absolute";<br>bgObj.style.top="0";<br>bgObj.style.background="#777";<br>bgObj.style.filter="progid:DXImageTransform.Microsoft.Alpha(style=2,opacity=25,finishOpacity=75";<br>bgObj.style.opacity="0.6";<br>bgObj.style.left="0";<br>bgObj.style.width=sWidth + "px";<br>bgObj.style.height=sHeight*2 + "px";<br>bgObj.style.zIndex = "10000";<br>document.body.appendChild(bgObj);//在body内添加该div对象<br>var msgObj=document.createElement("div")//创建一个div对象（提示框层）<br>//定义div属性，即相当于<br>//&lt;div id="msgDiv" align="center" style="background-color:white; border:1px solid #336699; position:absolute; left:50%; top:50%; font:12px/1.6em Verdana,Geneva,Arial,Helvetica,sans-serif; margin-left:-225px; margin-top:npx; width:400px; height:100px; text-align:center; line-height:25px; z-index:100001;"&gt;&lt;/div&gt;<br>msgObj.setAttribute("id","msgDiv");<br>msgObj.setAttribute("align","center");<br>msgObj.style.background="white";<br>msgObj.style.border="1px solid " + bordercolor;<br>msgObj.style.position = "absolute";<br>msgObj.style.left = "50%";<br>msgObj.style.top = "20%";<br>msgObj.style.font="12px/1.6em Verdana, Geneva, Arial, Helvetica, sans-serif";<br>msgObj.style.marginLeft = "-225px" ;<br>msgObj.style.marginTop = -75+document.documentElement.scrollTop+"px";<br>msgObj.style.width = msgw + "px";<br>msgObj.style.height =msgh + "px";<br>msgObj.style.textAlign = "center";<br>msgObj.style.lineHeight ="25px";<br>msgObj.style.zIndex = "10001";<br>var title=document.createElement("h4");//创建一个h4对象（提示框标题栏）<br>//定义h4的属性，即相当于<br>//&lt;h4 id="msgTitle" align="right" style="margin:0; padding:3px; background-color:#336699; filter:progid:DXImageTransform.Microsoft.Alpha(startX=20, startY=20, finishX=100, finishY=100,style=1,opacity=75,finishOpacity=100); opacity:0.75; border:1px solid #336699; height:18px; font:12px Verdana,Geneva,Arial,Helvetica,sans-serif; color:white; cursor:pointer;" onclick=""&gt;关闭&lt;/h4&gt;<br>title.setAttribute("id","msgTitle");<br>title.setAttribute("align","right");<br>title.style.margin="0";<br>title.style.padding="3px";<br>title.style.background=bordercolor;<br>title.style.filter="progid:DXImageTransform.Microsoft.Alpha(startX=20, startY=20, finishX=100, finishY=100,style=1,opacity=75,finishOpacity=100);";<br>title.style.opacity="0.75";<br>title.style.border="1px solid " + bordercolor;<br>title.style.height="18px";<br>title.style.font="12px Verdana, Geneva, Arial, Helvetica, sans-serif";<br>title.style.color="white";<br>title.style.cursor="pointer";<br>title.innerHTML="关闭";<br>title.onclick=removeObj;<br>var button=document.createElement("input");//创建一个input对象（提示框按钮）<br>//定义input的属性，即相当于<br>//&lt;input type="button" align="center" style="width:100px; align:center; margin-left:250px; margin-bottom:10px;" value="关闭"&gt;<br>button.setAttribute("type","button");<br>button.setAttribute("value","关闭");<br>button.style.width="60px";<br>button.style.align="center";<br>button.style.marginLeft="250px";<br>button.style.marginBottom="10px";<br>button.style.background=bordercolor;<br>button.style.border="1px solid "+ bordercolor;<br>button.style.color="white";<br>button.onclick=removeObj;<br>function removeObj(){//点击标题栏触发的事件<br>document.body.removeChild(bgObj);//删除背景层Div<br>document.getElementById("msgDiv").removeChild(title);//删除提示框的标题栏<br>document.body.removeChild(msgObj);//删除提示框层<br>}<br>document.body.appendChild(msgObj);//在body内添加提示框div对象msgObj<br>document.getElementById("msgDiv").appendChild(title);//在提示框div中添加标题栏对象title<br>var txt=document.createElement("p");//创建一个p对象（提示框提示信息）<br>//定义p的属性，即相当于<br>//&lt;p style="margin:1em 0;" id="msgTxt"&gt;测试效果&lt;/p&gt;<br>txt.style.margin="1em 0"<br>txt.setAttribute("id","msgTxt");<br>txt.innerHTML="&lt;a href=http://www.myziy.com&gt;&lt;img border=0 src="+str+"&gt;&lt;/img&gt;&lt;/a&gt;";//来源于函数调用时的参数值<br>document.getElementById("msgDiv").appendChild(txt);//在提示框div中添加提示信息对象txt<br>document.getElementById("msgDiv").appendChild(button);//在提示框div中添加按钮对象button<br>}<br>&lt;/script&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>&lt;p onclick="sAlert('logo.png','登录');"&gt;登陆&lt;/p&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</p><img src ="http://www.cnblogs.com/tivan/aggbug/1293747.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/42934/" target="_blank">[新闻]2008年10月11日科技博客精选</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>ASP.NET 常用代码</title><link>http://www.cnblogs.com/tivan/archive/2008/09/17/1292935.html</link><dc:creator>tivan</dc:creator><author>tivan</author><pubDate>Wed, 17 Sep 2008 15:45:00 GMT</pubDate><guid>http://www.cnblogs.com/tivan/archive/2008/09/17/1292935.html</guid><wfw:comment>http://www.cnblogs.com/tivan/comments/1292935.html</wfw:comment><comments>http://www.cnblogs.com/tivan/archive/2008/09/17/1292935.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/tivan/comments/commentRss/1292935.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/tivan/services/trackbacks/1292935.html</trackback:ping><description><![CDATA[<p>1. 打开新的窗口并传送参数：　　<br>传送参数： <p>response.write("&lt;script&gt;window.open(’*.aspx?id="+this.DropDownList1.SelectIndex+"&amp;id1="+...+"’)&lt;/script&gt;")  <p>　　接收参数： <p>string a = Request.QueryString("id");  <p>string b = Request.QueryString("id1");  <p>2.为按钮添加对话框 <p>Button1.Attributes.Add("onclick","return confirm(’确认?’)");  <p>button.attributes.add("onclick","if(confirm(’are you sure...?’)){return true;}else{return false;}")  <p>3.删除表格选定记录 <p>int intEmpID = (int)MyDataGrid.DataKeys[e.Item.ItemIndex];  <p>string deleteCmd = "DELETE from Employee where emp_id = " + intEmpID.ToString()  <p>4.删除表格记录警告 <p>private void DataGrid_ItemCreated(Object sender,DataGridItemEventArgs e)  <p>{  <p>switch(e.Item.ItemType)  <p>{  <p>case ListItemType.Item :  <p>case ListItemType.AlternatingItem :  <p>case ListItemType.EditItem:  <p>TableCell myTableCell;  <p>myTableCell = e.Item.Cells[14];  <p>LinkButton myDeleteButton ;  <p>myDeleteButton = (LinkButton)myTableCell.Controls[0];  <p>myDeleteButton.Attributes.Add("onclick","return confirm(’您是否确定要删除这条信息’);");  <p>break;  <p>default:  <p>break;  <p>}  <p>}  <p>5.点击表格行链接另一页 <p>private void grdCustomer_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)  <p>{  <p>//点击表格打开  <p>if (e.Item.ItemType == ListItemType.Item e.Item.ItemType == ListItemType.AlternatingItem)  <p>e.Item.Attributes.Add("onclick","window.open(’Default.aspx?id=" + e.Item.Cells[0].Text + "’);");  <p>}  <p>　　双击表格连接到另一页 <p>　　在itemDataBind事件中 <p>if(e.Item.ItemType == ListItemType.Item e.Item.ItemType == ListItemType.AlternatingItem)  <p>{  <p>string OrderItemID =e.item.cells[1].Text;  <p>...  <p>e.item.Attributes.Add("ondblclick", "location.href=’../ShippedGrid.aspx?id=" + OrderItemID + "’");  <p>}  <p>　　双击表格打开新一页 <p>if(e.Item.ItemType == ListItemType.Item e.Item.ItemType == ListItemType.AlternatingItem)  <p>{  <p>string OrderItemID =e.item.cells[1].Text;  <p>...  <p>e.item.Attributes.Add("ondblclick", "open(’../ShippedGrid.aspx?id=" + OrderItemID + "’)");  <p>}  <p>6.表格超连接列传递参数 <p>&lt;asp:HyperLinkColumn Target="_blank" headertext="ID号" DataTextField="id" NavigateUrl="aaa.aspx?id='  <p>&lt;%# DataBinder.Eval(Container.DataItem, "数据字段1")%&gt;' &amp; name='&lt;%# DataBinder.Eval(Container.DataItem, "数据字段2")%&gt;' /&gt;  <p>7.表格点击改变颜色 <p>if (e.Item.ItemType == ListItemType.Item e.Item.ItemType == ListItemType.AlternatingItem)  <p>{  <p>e.Item.Attributes.Add("onclick","this.style.backgroundColor='#99cc00';this.style.color='buttontext';this.style.cursor='default';");  <p>}  <p>　　写在DataGrid的_ItemDataBound里 <p>if (e.Item.ItemType == ListItemType.Item e.Item.ItemType == ListItemType.AlternatingItem)  <p>{  <p>e.Item.Attributes.Add("onmouseover","this.style.backgroundColor='#99cc00';this.style.color='buttontext';this.style.cursor='default';");  <p>e.Item.Attributes.Add("onmouseout","this.style.backgroundColor='';this.style.color='';");  <p>}  <p>8.关于日期格式 <p>　　日期格式设定 <p>DataFormatString="{0:yyyy-MM-dd}" <br>　　我觉得应该在itembound事件中 <p>e.items.cell["你的列"].text=DateTime.Parse(e.items.cell["你的列"].text.ToString("yyyy-MM-dd")) <br>9.获取错误信息并到指定页面 <p>　　不要使用Response.Redirect,而应该使用Server.Transfer  <p>e.g  <p>// in global.asax <br>protected void Application_Error(Object sender, EventArgs e) { <br>if (Server.GetLastError() is HttpUnhandledException) <br>Server.Transfer("MyErrorPage.aspx");  <p>//其余的非HttpUnhandledException异常交给ASP.NET自己处理就okay了 :) <br>} <br>Redirect会导致post－back的产生从而丢失了错误信息，所以页面导向应该直接在服务器端执行，这样就可以在错误处理页面得到出错信息并进行相应的处理 <p>10.清空Cookie <p>Cookie.Expires=[DateTime]; <br>Response.Cookies("UserName").Expires = 0 <br>11.自定义异常处理 <p>//自定义异常处理类  <p>using System;  <p>using System.Diagnostics; <p>namespace MyAppException <p>{ <p>/// &lt;summary&gt;  <p>/// 从系统异常类ApplicationException继承的应用程序异常处理类。  <p>/// 自动将异常内容记录到Windows NT/2000的应用程序日志  <p>/// &lt;/summary&gt;  <p>public class AppException : System.ApplicationException <p>{ <p>public AppException() <p>{ <p>if (ApplicationConfiguration.EventLogEnabled) LogEvent("出现一个未知错误。"); <p>} <p>public AppException(string message) <p>{ <p>LogEvent(message); <p>} <p>public AppException(string message, Exception innerException) <p>{ <p>LogEvent(message); <p>if (innerException != null) <p>{ <p>LogEvent(innerException.Message); <p>} <p>} <p>} <p>} <p>//日志记录类  <p>using System; <p>using System.Configuration; <p>using System.Diagnostics; <p>using System.IO; <p>using System.Text; <p>using System.Threading; <p>namespace MyEventLog <p>{ <p>/// &lt;summary&gt;  <p>/// 事件日志记录类，提供事件日志记录支持  <p>/// &lt;remarks&gt;  <p>/// 定义了4个日志记录方法 (error, warning, info, trace)  <p>/// &lt;/remarks&gt;  <p>/// &lt;/summary&gt;  <p>public class ApplicationLog <p>{ <p>/// &lt;summary&gt;  <p>/// 将错误信息记录到Win2000/NT事件日志中  <p>/// &lt;param name="message"&gt;需要记录的文本信息&lt;/param&gt;  <p>/// &lt;/summary&gt;  <p>public static void WriteError(String message) <p>{ <p>WriteLog(TraceLevel.Error, message); <p>} <p>/// &lt;summary&gt;  <p>/// 将警告信息记录到Win2000/NT事件日志中  <p>/// &lt;param name="message"&gt;需要记录的文本信息&lt;/param&gt;  <p>/// &lt;/summary&gt;  <p>public static void WriteWarning(String message) <p>{ <p>WriteLog(TraceLevel.Warning, message); <p>} <p>/// &lt;summary&gt;  <p>/// 将提示信息记录到Win2000/NT事件日志中  <p>/// &lt;param name="message"&gt;需要记录的文本信息&lt;/param&gt;  <p>/// &lt;/summary&gt;  <p>public static void WriteInfo(String message) <p>{ <p>WriteLog(TraceLevel.Info, message); <p>} <p>/// &lt;summary&gt;  <p>/// 将跟踪信息记录到Win2000/NT事件日志中  <p>/// &lt;param name="message"&gt;需要记录的文本信息&lt;/param&gt;  <p>/// &lt;/summary&gt;  <p>public static void WriteTrace(String message) <p>{ <p>WriteLog(TraceLevel.Verbose, message); <p>} <p>/// &lt;summary&gt;  <p>/// 格式化记录到事件日志的文本信息格式  <p>/// &lt;param name="ex"&gt;需要格式化的异常对象&lt;/param&gt;  <p>/// &lt;param name="catchInfo"&gt;异常信息标题字符串.&lt;/param&gt;  <p>/// &lt;retvalue&gt;  <p>/// &lt;para&gt;格式后的异常信息字符串，包括异常内容和跟踪堆栈.&lt;/para&gt;  <p>/// &lt;/retvalue&gt;  <p>/// &lt;/summary&gt;  <p>public static String FormatException(Exception ex, String catchInfo) <p>{ <p>StringBuilder strBuilder = new StringBuilder(); <p>if (catchInfo != String.Empty) <p>{ <p>strBuilder.Append(catchInfo).Append("\r\n"); <p>} <p>strBuilder.Append(ex.Message).Append("\r\n").Append(ex.StackTrace); <p>return strBuilder.ToString(); <p>} <p>/// &lt;summary&gt;  <p>/// 实际事件日志写入方法  <p>/// &lt;param name="level"&gt;要记录信息的级别（error,warning,info,trace).&lt;/param&gt;  <p>/// &lt;param name="messageText"&gt;要记录的文本.&lt;/param&gt;  <p>/// &lt;/summary&gt;  <p>private static void WriteLog(TraceLevel level, String messageText) <p>{ <p>try <p>{ <p>EventLogEntryType LogEntryType; <p>switch (level) <p>{ <p>case TraceLevel.Error: <p>LogEntryType = EventLogEntryType.Error; <p>break; <p>case TraceLevel.Warning: <p>LogEntryType = EventLogEntryType.Warning; <p>break; <p>case TraceLevel.Info: <p>LogEntryType = EventLogEntryType.Information; <p>break; <p>case TraceLevel.Verbose: <p>LogEntryType = EventLogEntryType.SuccessAudit; <p>break; <p>default: <p>LogEntryType = EventLogEntryType.SuccessAudit; <p>break; <p>} <p>EventLog eventLog = new EventLog("Application", ApplicationConfiguration.EventLogMachineName, ApplicationConfiguration.EventLogSourceName); <p>//写入事件日志  <p>eventLog.WriteEntry(messageText, LogEntryType); <p>} <p>catch { } //忽略任何异常  <p>} <p>} //class ApplicationLog  <p>} <p>12.Panel 横向滚动，纵向自动扩展 <p>&lt;asp:panel style="overflow-x:scroll;overflow-y:auto;"&gt;&lt;/asp:panel&gt;  <p>13.回车转换成Tab <p>&lt;script language="javascript" for="document" event="onkeydown"&gt; <br>if(event.keyCode==13 &amp;&amp; event.srcElement.type!='button' &amp;&amp; event.srcElement.type!='submit' &amp;&amp; event.srcElement.type!='reset' &amp;&amp; event.srcElement.type!=''&amp;&amp; event.srcElement.type!='textarea'); <br>event.keyCode=9; <br>&lt;/script&gt;  <p>onkeydown="if(event.keyCode==13) event.keyCode=9"  <p>14.DataGrid超级连接列 <p>DataNavigateUrlField="字段名" DataNavigateUrlFormatString="<a href="http://xx/inc/delete.aspx?ID=%7b0">http://xx/inc/delete.aspx?ID={0</a>}"  <p>15.DataGrid行随鼠标变色 <p>private void DGzf_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)  <p>{  <p>if (e.Item.ItemType!=ListItemType.Header)  <p>{  <p>e.Item.Attributes.Add( "onmouseout","this.style.backgroundColor=\""+e.Item.Style["BACKGROUND-COLOR"]+"\"");  <p>e.Item.Attributes.Add( "onmouseover","this.style.backgroundColor=\""+ "#EFF3F7"+"\"");  <p>}  <p>}  <p>16.模板列 <p>&lt;ASP:TEMPLATECOLUMN visible="False" sortexpression="demo" headertext="ID"&gt; <p>&lt;ITEMTEMPLATE&gt; <p>&lt;ASP:LABEL text='&lt;%# DataBinder.Eval(Container.DataItem, "ArticleID")%&gt;' runat="server" width="80%" id="lblColumn" /&gt; <p>&lt;/ITEMTEMPLATE&gt; <p>&lt;/ASP:TEMPLATECOLUMN&gt; <p>&lt;ASP:TEMPLATECOLUMN headertext="选中"&gt; <p>&lt;HEADERSTYLE wrap="False" horizontalalign="Center"&gt;&lt;/HEADERSTYLE&gt; <p>&lt;ITEMTEMPLATE&gt; <p>&lt;ASP:CHECKBOX id="chkExport" runat="server" /&gt; <p>&lt;/ITEMTEMPLATE&gt; <p>&lt;EDITITEMTEMPLATE&gt; <p>&lt;ASP:CHECKBOX id="chkExportON" runat="server" enabled="true" /&gt; <p>&lt;/EDITITEMTEMPLATE&gt; <p>&lt;/ASP:TEMPLATECOLUMN&gt; <p>后台代码 <p>protected void CheckAll_CheckedChanged(object sender, System.EventArgs e)  <p>{  <p>//改变列的选定，实现全选或全不选。  <p>CheckBox chkExport ;  <p>if( CheckAll.Checked)  <p>{  <p>foreach(DataGridItem oDataGridItem in MyDataGrid.Items)  <p>{  <p>chkExport = (CheckBox)oDataGridItem.FindControl("chkExport");  <p>chkExport.Checked = true;  <p>}  <p>}  <p>else <p>{  <p>foreach(DataGridItem oDataGridItem in MyDataGrid.Items)  <p>{  <p>chkExport = (CheckBox)oDataGridItem.FindControl("chkExport");  <p>chkExport.Checked = false;  <p>}  <p>}  <p>}  <p>17.数字格式化 <p>　　【&lt;%#Container.DataItem("price")%&gt;的结果是500.0000，怎样格式化为500.00?】 <p>&lt;%#Container.DataItem("price","{0:￥#,##0.00}")%&gt;  <p>int i=123456; <br>string s=i.ToString("###,###.00"); <br>18.日期格式化 <p>　　【aspx页面内：&lt;%# DataBinder.Eval(Container.DataItem,"Company_Ureg_Date")%&gt;  <p>　　显示为： 2004-8-11 19:44:28  <p>　　我只想要：2004-8-11 】 <p>&lt;%# DataBinder.Eval(Container.DataItem,"Company_Ureg_Date","{0:yyyy-M-d}")%&gt; <br>　　应该如何改？ <p>　　【格式化日期】 <p>　　取出来,一般是object((DateTime)objectFromDB).ToString("yyyy-MM-dd");  <p>　　【日期的验证表达式】 <p>A.以下正确的输入格式： [2004-2-29], [2004-02-29 10:29:39 pm], [2004/12/31]  <p>^((\d{2}(([02468][048])([13579][26]))[\-\/\s]?((((0?[13578])(1[02]))[\-\/\s]?((0?[1-9])([1-2][0-9])(3[01])))(((0?[469])(11))[\-\/\s]?((0?[1-9])([1-2][0-9])(30)))(0?2[\-\/\s]?((0?[1-9])([1-2][0-9])))))(\d{2}(([02468][1235679])([13579][01345789]))[\-\/\s]?((((0?[13578])(1[02]))[\-\/\s]?((0?[1-9])([1-2][0-9])(3[01])))(((0?[469])(11))[\-\/\s]?((0?[1-9])([1-2][0-9])(30)))(0?2[\-\/\s]?((0?[1-9])(1[0-9])(2[0-8]))))))(\s(((0?[1-9])(1[0-2]))\:([0-5][0-9])((\s)(\:([0-5][0-9])\s))([AMPMampm]{2,2})))?$ <br>B.以下正确的输入格式：[0001-12-31], [9999 09 30], [2002/03/03]  <p>^\d{4}[\-\/\s]?((((0[13578])(1[02]))[\-\/\s]?(([0-2][0-9])(3[01])))(((0[469])(11))[\-\/\s]?(([0-2][0-9])(30)))(02[\-\/\s]?[0-2][0-9]))$ <br>　　【大小写转换】 <p>HttpUtility.HtmlEncode(string); <br>HttpUtility.HtmlDecode(string) <br>19.如何设定全局变量 <p>Global.asax中 <p>Application_Start()事件中 <p>　　添加Application[属性名] ＝ xxx;  <p>　　就是你的全局变量 <p>20.怎样作到HyperLinkColumn生成的连接后，点击连接，打开新窗口？ <p>HyperLinkColumn有个属性Target,将器值设置成"_blank"即可.(Target="_blank")  <p>　　【ASPNETMENU】点击菜单项弹出新窗口 <p>　　在你的menuData.xml文件的菜单项中加入URLTarget="_blank"，如： <p>&lt;?xml version="1.0" encoding="GB2312"?&gt; <br>&lt;MenuData ImagesBaseURL="images/"&gt; <br>&lt;MenuGroup&gt; <br>&lt;MenuItem Label="内参信息" URL="Infomation.aspx" &gt; <br>&lt;MenuGroup ID="BBC"&gt; <br>&lt;MenuItem Label="公告信息" URL="Infomation.aspx" URLTarget="_blank" LeftIcon="file.gif"/&gt; <br>&lt;MenuItem Label="编制信息简报" URL="NewInfo.aspx" LeftIcon="file.gif" /&gt; <br>...... <br>　　最好将你的aspnetmenu升级到1.2版 <p>21.读取DataGrid控件TextBox值 <p>foreach(DataGrid dgi in yourDataGrid.Items) <br>{ <br>TextBox tb = (TextBox)dgi.FindControl("yourTextBoxId"); <br>tb.Text.... <br>}  <p>23.在DataGrid中有3个模板列包含Textbox分别为 DG_ShuLiang (数量) DG_DanJian(单价) DG_JinE(金额)分别在5.6.7列，要求在录入数量及单价的时候自动算出金额即:数量*单价=金额还要求录入时限制为数值型.我如何用客户端脚本实现这个功能?  <p>&lt;asp:TemplateColumn HeaderText="数量"&gt;<br>&lt;ItemTemplate&gt;<br>&lt;asp:TextBox id="ShuLiang" runat='server' Text='&lt;%# DataBinder.Eval(Container.DataItem,"DG_ShuLiang")%&gt;'<br>onkeyup="javascript:DoCal()"<br>/&gt;<br>&lt;asp:RegularExpressionValidator id="revS" runat="server" ControlToValidate="ShuLiang" ErrorMessage="must be integer" ValidationExpression="^\d+$" /&gt;<br>&lt;/ItemTemplate&gt;<br>&lt;/asp:TemplateColumn&gt;<br>&lt;asp:TemplateColumn HeaderText="单价"&gt;<br>&lt;ItemTemplate&gt;<br>&lt;asp:TextBox id="DanJian" runat='server' Text='&lt;%# DataBinder.Eval(Container.DataItem,"DG_DanJian")%&gt;'<br>onkeyup="javascript:DoCal()"<br>/&gt;<br>&lt;asp:RegularExpressionValidator id="revS2" runat="server" ControlToValidate="DanJian" ErrorMessage="must be numeric" ValidationExpression="^\d+(\.\d*)?$" /&gt;<br>&lt;/ItemTemplate&gt;<br>&lt;/asp:TemplateColumn&gt;<br>&lt;asp:TemplateColumn HeaderText="金额"&gt;<br>&lt;ItemTemplate&gt;<br>&lt;asp:TextBox id="JinE" runat='server' Text='&lt;%# DataBinder.Eval(Container.DataItem,"DG_JinE")%&gt;' /&gt;<br>&lt;/ItemTemplate&gt;<br>&lt;/asp:TemplateColumn&gt;&lt;script language="javascript"&gt;<br>function DoCal() <br>{ <br>var e = event.srcElement; <br>var row = e.parentNode.parentNode; <br>var txts = row.all.tags("INPUT"); <br>if (!txts.length txts.length &lt; 3) <br>return; <br>var q = txts[txts.length-3].value; <br>var p = txts[txts.length-2].value; <br>if (isNaN(q) isNaN(p)) <br>return; <br>q = parseInt(q); <br>p = parseFloat(p); <br>txts[txts.length-1].value = (q * p).toFixed(2); <br>} <br>&lt;/script&gt; <p>24.datagrid选定比较底下的行时，为什么总是刷新一下，然后就滚动到了最上面，刚才选定的行因屏幕的关系就看不到了。 <p>page_load <br>page.smartNavigation=true  <p>25.在Datagrid中修改数据，当点击编辑键时，数据出现在文本框中，怎么控制文本框的大小 ?  <p>private void DataGrid1_ItemDataBound(obj sender, DataGridItemEventArgs e)<br>{<br>for (int i = 0; i &lt; e.Item.Cells.Count - 1; i++)<br>if (e.Item.ItemType == ListItemType.EditType)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.Item.Cells[i].Attributes.Add("Width", "80px");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>}&nbsp; <p>26.对话框 <p>private static string ScriptBegin = "&lt;script language=\"JavaScript\"&gt;"; <p>private static string ScriptEnd = "&lt;/script&gt;"; <p>public static void ConfirmMessageBox(string PageTarget, string Content) <p>{ <p>string ConfirmContent = "var retValue=window.confirm('" + Content + "');" + "if(retValue){window.location='" + PageTarget + "';}"; <p>ConfirmContent = ScriptBegin + ConfirmContent + ScriptEnd; <p>Page ParameterPage = (Page)System.Web.HttpContext.Current.Handler; <p>ParameterPage.RegisterStartupScript("confirm", ConfirmContent); <p>//Response.Write(strScript);  <p>} <p>27. 将时间格式化：string aa=DateTime.Now.ToString("yyyy年MM月dd日");  <p>1.1 取当前年月日时分秒 <p>currentTime=System.DateTime.Now; <br>1.2 取当前年 <p>int 年= DateTime.Now.Year; <br>1.3 取当前月 <p>int 月= DateTime.Now.Month; <br>1.4 取当前日 <p>int 日= DateTime.Now.Day; <br>1.5 取当前时 <p>int 时= DateTime.Now.Hour; <br>1.6 取当前分 <p>int 分= DateTime.Now.Minute; <br>1.7 取当前秒 <p>int 秒= DateTime.Now.Second; <br>1.8 取当前毫秒 <p>int 毫秒= DateTime.Now.Millisecond; <br>28．自定义分页代码： <p>　　先定义变量 ： <p>public static int pageCount; //总页面数<br>public static int curPageIndex=1; //当前页面<br>　　下一页： <p>if(DataGrid1.CurrentPageIndex &lt; (DataGrid1.PageCount - 1)) <br>{ <br>DataGrid1.CurrentPageIndex += 1; <br>curPageIndex+=1; <br>}  <p>bind(); // DataGrid1数据绑定函数<br>　　上一页： <p>if(DataGrid1.CurrentPageIndex &gt;0) <br>{ <br>DataGrid1.CurrentPageIndex += 1; <br>curPageIndex-=1; <br>}  <p>bind(); // DataGrid1数据绑定函数<br>　　直接页面跳转： <p>int a=int.Parse(JumpPage.Value.Trim());//JumpPage.Value.Trim()为跳转值 <p>if(a&lt;DataGrid1.PageCount) <br>{ <br>this.DataGrid1.CurrentPageIndex=a; <br>}  <p>bind(); <br>29．DataGrid使用： <p>　　添加删除确认： <p>private void DataGrid1_ItemCreated(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e) <br>{ <br>foreach(DataGridItem di in this.DataGrid1.Items) <br>{ <br>if(di.ItemType==ListItemType.Itemdi.ItemType==ListItemType.AlternatingItem) <br>{ <br>((LinkButton)di.Cells[8].Controls[0]).Attributes.Add("onclick","return confirm('确认删除此项吗?');"); <br>} <br>} <br>} <br>　　样式交替： <p>ListItemType itemType = e.Item.ItemType;  <p>if (itemType == ListItemType.Item ) <br>{ <br>e.Item.Attributes["onmouseout"] = "javascript:this.style.backgroundColor='#FFFFFF';"; <br>e.Item.Attributes["onmouseover"] = "javascript:this.style.backgroundColor='#d9ece1';cursor='hand';" ; <br>} <br>else if( itemType == ListItemType.AlternatingItem) <br>{ <br>e.Item.Attributes["onmouseout"] = "javascript:this.style.backgroundColor='#a0d7c4';"; <br>e.Item.Attributes["onmouseover"] = "javascript:this.style.backgroundColor='#d9ece1';cursor='hand';" ; <br>} <br>　　添加一个编号列： <p>DataTable dt= c.ExecuteRtnTableForAccess(sqltxt); //执行sql返回的DataTable <br>DataColumn dc=dt.Columns.Add("number",System.Type.GetType("System.String"));  <p>for(int i=0;i&lt;dt.Rows.Count;i++) <br>{ <br>dt.Rows[i]["number"]=(i+1).ToString(); <br>}  <p>DataGrid1.DataSource=dt; <br>DataGrid1.DataBind(); <br>DataGrid1中添加一个CheckBox，页面中添加一个全选框 <p>private void CheckBox2_CheckedChanged(object sender, System.EventArgs e) <br>{ <br>foreach(DataGridItem thisitem in DataGrid1.Items) <br>{ <br>((CheckBox)thisitem.Cells[0].Controls[1]).Checked=CheckBox2.Checked; <br>} <br>} <br>　　将当前页面中DataGrid1显示的数据全部删除 <p>foreach(DataGridItem thisitem in DataGrid1.Items) <br>{ <br>if(((CheckBox)thisitem.Cells[0].Controls[1]).Checked) <br>{ <br>string strloginid= DataGrid1.DataKeys[thisitem.ItemIndex].ToString(); <br>Del (strloginid); //删除函数<br>} <br>} <br>30．当文件在不同目录下，需要获取数据库连接字符串（如果连接字符串放在Web.config，然后在Global.asax中初始化） <p>　　在Application_Start中添加以下代码： <p>Application["ConnStr"]=this.Context.Request.PhysicalApplicationPath+ConfigurationSettings. <br>AppSettings["ConnStr"].ToString(); <br>31． 变量.ToString()  <p>　　字符型转换 转为字符串 <p>12345.ToString("n"); //生成 12,345.00 <br>12345.ToString("C"); //生成 ￥12,345.00 <br>12345.ToString("e"); //生成 1.234500e+004 <br>12345.ToString("f4"); //生成 12345.0000 <br>12345.ToString("x"); //生成 3039 (16进制) <br>12345.ToString("p"); //生成 1,234,500.00% <br>32、变量.Substring(参数1,参数2); <p>　　截取字串的一部分，参数1为左起始位数，参数2为截取几位。 如：string s1 = str.Substring(0,2);  <p>33．在自己的网站上登陆其他网站：(如果你的页面是通过嵌套方式的话，因为一个页面只能有一个FORM，这时可以导向另外一个页面再提交登陆信息) <p>&lt;SCRIPT language="javascript"&gt; <br>&lt;!-- <br>function gook(pws) <br>{ <br>frm.submit(); <br>} <br>//--&gt;  <p>&lt;/SCRIPT&gt; &lt;body leftMargin="0" topMargin="0" onload="javascript:gook()" marginwidth="0" marginheight="0"&gt; <br>&lt;form name="frm" action=" <a href="http://220.194.55.68:6080/login.php?retid=7259">http://220.194.55.68:6080/login.php?retid=7259</a> " method="post"&gt; <br>&lt;tr&gt; <br>&lt;td&gt; <br>&lt;input id="f_user" type="hidden" size="1" name="f_user" runat="server"&gt; <br>&lt;input id="f_domain" type="hidden" size="1" name="f_domain" runat="server"&gt; <br>&lt;input class="box" id="f_pass" type="hidden" size="1" name="pwshow" runat="server"&gt;  <p>&lt;INPUT id="lng" type="hidden" maxLength="20" size="1" value="5" name="lng"&gt; <br>&lt;INPUT id="tem" type="hidden" size="1" value="2" name="tem"&gt;  <p>&lt;/td&gt;  <p>&lt;/tr&gt;  <p>&lt;/form&gt; <br>　　文本框的名称必须是你要登陆的网页上的名称，如果源码不行可以用vsniffer 看看。 <p>　　下面是获取用户输入的登陆信息的代码： <p>string name; <br>name=Request.QueryString["EmailName"];  <p>try <br>{ <br>int a=name.IndexOf("@",0,name.Length); <br>f_user.Value=name.Substring(0,a); <br>f_domain.Value=name.Substring(a+1,name.Length-(a+1)); <br>f_pass.Value=Request.QueryString["Psw"]; <br>}  <p>catch <br>{ <br>Script.Alert("错误的邮箱!"); <br>Server.Transfer("index.aspx"); <br>}</p><img src ="http://www.cnblogs.com/tivan/aggbug/1292935.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/42933/" target="_blank">[新闻]搞死开心网还是搞活他？</a><br/><a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻频道</a>&nbsp;<a href="http://space.cnblogs.com/group.htm" target="_blank">小组</a>&nbsp;<a href="http://space.cnblogs.com/q" target="_blank">博问</a>&nbsp;<a href="http://wz.cnblogs.com/" target="_blank">网摘</a>&nbsp;<a href="http://space.cnblogs.com/ing" target="_blank">闪存</a>]]></description></item><item><title>Membership学习</title><link>http://www.cnblogs.com/tivan/archive/2008/09/17/1292903.html</link><dc:creator>tivan</dc:creator><author>tivan</author><pubDate>Wed, 17 Sep 2008 14:43:00 GMT</pubDate><guid>http://www.cnblogs.com/tivan/archive/2008/09/17/1292903.html</guid><wfw:comment>http://www.cnblogs.com/tivan/comments/1292903.html</wfw:comment><comments>http://www.cnblogs.com/tivan/archive/2008/09/17/1292903.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/tivan/comments/commentRss/1292903.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/tivan/services/trackbacks/1292903.html</trackback:ping><description><![CDATA[<p>转自：<a title="http://www.cnblogs.com/dotLive/" href="http://www.cnblogs.com/dotLive/">http://www.cnblogs.com/dotLive/</a>  <p>&nbsp; <p>Membership学习（一） Membership介绍<br>Asp.net的membership提供了一种存储，验证和管理用户信息以及权限的统一的路径。membership所对应的验证方式是asp.net的Forms身份验证（注：asp.net的身份验证方式 有好几种包括windows身份验证 Forms身份验证 Passport身份认证当然还有就是没有验证）。在vs2005中 还给membership提供了一系列的控件(login控件)，能方便的在asp.net实现membership。<br>具体来说membership能实现：<br>1． 创建用户<br>2． 可以保存membership信息在sql server,Active Directory以及其他的一些数据保存方法<br>3． 鉴别谁在访问你的网站。如果使用login控件，几乎可以在不写代码的情况下完成。<br>4． 管理密码。包括 创建 修改 重置<br>5． 创建一个唯一的值来标志每一个登陆用户，可以实现对用户的个性化以及角色管理。<br>6． 提供一个用户自定义的membership provider，从而实现一些自己网站特有的数据的保存和管理。  <p>&nbsp;&nbsp;&nbsp; 让membership能工作要做的一些工作：<br>1． 在web.config中填写一些membership的设置，在asp.net默认情况下membership是被允许使用的 而默认的数据保存是使用ms的sql server.你可以做些设置提供其他的数据保存方法 包括自定义的方法。这将在后面的文章中具体介绍<br>2． 设置你的网站使用Forms验证方式，<br>3． 为membership定义用户帐号。可以使用vs2005提供的web administrator tool进行设置，也可以自己制作create user页面进行，而自定义页面 只要调用membership.createuser就可以方便的创建用户。  <p>Membership的管理和配置：<br>在web.config中配置管理membership最简单的方法就是使用Web Site Administration Tool（在vs2005的websits菜单里）。你可以指定membership的提供者sql server还是其他，密码的管理 包括是否要加密保存 以及是否要给用户提供根据事先设置的问题 恢复密码的机制。当然是用web site administration tool可以直接创建和管理用户及角色。  <p>Membership的方便之处在于，当一个用户通过认证之后，他的信息的保存都是系统自动完成的 这样我以前最头痛的如何安全完整的在各个页面中传递登陆用户信息这点就迎刃而解了。  <p>&nbsp;&nbsp;&nbsp; 如果使用vs2005自带的login控件 要清楚一件事 就是login控件的功能实现都是调用了membership的类函数，我们完全可以自己写出所有的控件。  <p>之后会具体介绍membership的使用。我的介绍也是对msdn的一些翻译和整理。  <p>路漫漫其修远兮 吾将上下而求索  <p>&nbsp;</p> <p>&nbsp;</p> <p>Membership学习（二）membership入门  <p>--不写一行代码在asp.net中实现用户验证管理系统  <p>这篇文章我们将实现一个简单的网站，在网站中实现用户的身份验证，创建用户，修改密码　还有限制匿名用户访问某些目录等功能，最神奇的是使用ａｓｐ．ｎｅｔ２．０实现我们几乎不用手工写一行代码。--不知道以后程序员要做些什么事了：（！！  <p>我们手工从头创建一个web应用，学习其中的一些技术，这个应用将要完成的任务有  <p>1． 创建一个包含membership服务的web应用，创建一个用户  <p>2． 使用login控件，得到用户的凭证以及显示登录用户的信息  <p>3． 在网站里创建一个目录 里面的页面只有登录用户才能访问  <p>4． 允许网站创建新用户  <p>5． 用户可以修改和重置  <p>我的开发环境windows2003,iis6.0,vs2005 team suit英文版，sql server2005 express  <p>工作开始  <p>一、在本地IIS上创建一个网站  <p>1． 打开visual studio,file菜单，选择 New web site  <p>2． 选择asp.net web site,在location下来框里选择http，然后点击browser按钮，在弹出框里选择local IIS,打开Local Web Server,选择默认网站（default web site）,点击对话框右上的<b>Create New Web Application</b>图标，命名为membership,点击open按钮关闭对话框。  <p>3． 选择工作语言（c#等），以后在创建其他网页时 可以选择不同的语言 ：）  <p>4． 点击OK  <p>网站创建后 会默认生成一个default.aspx页面 我们留着他就可以了，也可以删除他 重新创建一个页面  <p>二、配置membership  <p>1. 创建一个新的文件夹在网站里命名为MemberPages  <p>2. 创建一个membeship用户  <p>a. 在菜单website里选择Asp.net configuration,在打开的网页里选择Security tab页，点击<b>Use the security Setup Wizard to configure security step by step</b>链接。  <p>b. 在向导第2步里选择 <b>From the Internet </b>选项 这里是让你的网站使用Form的身份认证，原因在membership介绍 文章里讲过。  <p>c. 点击Next 这步还是默认使用sql server2005 express并会在<b>App_Data</b>目录里生成数据库文件。  <p>d. 点击Next 这里不要选择<b>Enable roles for this web site</b>  <p>e. 点击Next 这时是创建一个用户 输入User Name ,Password,,E-mail,<b> S</b>ecurity Question and Security Answer ,创建用户。这里顺便说一下 membership默认的密码设置是比较严格的需要字母数字加特殊字符，其实我们可以在web.config中给membership的密码指定一个正则表达式 来修改密码的规则。<a href="http://msdn2.microsoft.com/zh-CN/library/system.web.security.membership.passwordstrengthregularexpression.aspx">具体可以在msdn中查找</a>  <p>3. 创建一个规则 限制访问一个指定的目录  <p>a. 接着刚才的向导继续next,进入创建访问规则页面，在显示网站目录的框里，展开目录，选择刚才我们创建的MemberPages的文件夹。  <p>b. 在<b>Rule applies to</b>下选择<b>Anonymous users</b><b>，</b>在<b>Permission</b>下选择ｄｅｎｙ，这样就限制了匿名用户访问目录的权限。  <p>c. 点击ａｄｄ　ｔｈｉｓ　ｒｕｌｅ权限创建，接着是Ｆｉｎｉｓｈ．  <p>三、为应用配置一个E-mail  <p>这个操作的目的是为了恢复密码时发送密码给用户  <p>还是在asp.net configuration的网页里 选择Application tab页面，在Smtp Settings下 点击<b>Configure SMTP e-mail settings</b>.链接，这个配置一个smtp服务器以及一个email账号。  <p>配置完email就可以关闭 这个配置页面了。  <p>四、用户登录  <p>打开default.aspx页面，使用design视图，在上面写个welcome什么的，然后拖上一个login控件组里的loginstatus控件。接着建一个login.aspx页面，在这个例子里我们的页面的名字一定要取成login.aspx，在默认情况下 当匿名用户访问受限制的页面时，会自动转到login.aspx，这个默认设置可以通过配置更改的，这里我们就使用默认。  <p>在login.aspx页面上拖放一个login控件组里的login控件，再放置一个<b>ValidationSummary</b>控件，用来现实填写的出错信息，将ValidationSummary的ValidationGroup属性设置成login控件的id名称。  <p>显示登录用户信息  <p>选择default.aspx页面拖上一个loginview控件，使用loginview智能标签(smart tag)，选择模板<b>AnonymousTemplate</b>,写上”你没有登录，点击login链接登录”，然后再选择模板<b>LoggedInTemplate</b>,写上”欢迎”字符，再在后面拖上一个loginname控件。  <p>这之后，你可以先测试一下你的页面 使用我们上面建立的用户进行登录，看看不同的显示。  <p>五、建立只能用户访问的页面  <p>在我们上面建立的MemberPages文件夹里建立一个Members.aspx页面，写上welcome member,然后 我们在default.aspx页面上拖一个HyperLInk控件，在NavigateUrl属性里写上~/memberPages/Members.aspx  <p>这里也可以进行一次测试  <p>六、创建新用户  <p>在根目录里建立一个register.aspx,在页面上拖放一个CreateUserWizard控件并把它的ContinueDestinationPageUrl属性设置成~/default.aspx,这个属性是对你点击Finish之后跳转的页面。接着在页面里添加一个Hyperlink控件,NavigateUrl设置成~/default.aspx.最后在default.aspx页面里添加一个指向register.aspx页面的链接，为了好看一点 我们把这个链接放到loginview控件的AnonymousTemplate模板里。  <p>测试一下  <p>七、修改密码  <p>在MemberPages文件夹里创建一个ChangePassword.aspx文件，拖上一个ChangePassword控件也把它的ContinueDestinationPageUrl属性设置成~/default.aspx。接着在default.aspx页面里添加一个指向changepassword.aspx页面的链接， 我们把这个链接放到loginview控件的LoggedInTemplate模板里。  <p>测试  <p>八、重置密码  <p>在根目录里建立一个recoverypassword.aspx文件，拖一个passwordrecovery控件，并放一个链接指向default.aspx.之后在default.aspx的loginview控件的AnonymousTemplate模板里添加一个链接指向recoverypassword控件。  <p>测试  <p>这样整个例子完成 大家也可以看到 我们没有写一行代码。：）  <p>&nbsp;</p> <p>&nbsp;</p> <p>本来想在第三篇文章里介绍一下 Membership的类的，不过现在中文msdn也出来了，所以就不写了，，直接到介绍Membership Providers。<br>Membership Providers提供了Membership数据源和服务之间的所有接口，在Asp.net2.0中提供了两个Provider：SqlMembershipProvider和ActiveDirectoryMembershipProvider，从命名中我们也可以看出，SqlMembershipProvider是把 sql server和sql server express数据库作为数据库源，而ActiveDirectoryMembershipProvider是有Microsoft Active Directory(活动目录)作为数据源的。<br>Membership Providers的基本工作是用来管理一个网站注册用的数据，并且提供一些函数用来做 创建，删除用户，验证用户登录信息，修改密码等工作。在.net 框架的System.Web.Security命名空间下有一个MembershipUser的类，这个类定义了Membership 用户的一些基本属性，Membership Provider用这个类来描绘每个用户信息。Membership Providers 有一个基础类 他的定义如下：（丛msdn里copy出来的）  <p><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top">public abstract class MembershipProvider : ProviderBase<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"><img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> // Abstract properties<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract bool EnablePasswordRetrieval <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract bool EnablePasswordReset <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract bool RequiresQuestionAndAnswer <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract string ApplicationName <img src="http://www.cnblogs.com/Images/dot.gif">{ get; set; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract int MaxInvalidPasswordAttempts <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract int PasswordAttemptWindow <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract bool RequiresUniqueEmail <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract MembershipPasswordFormat PasswordFormat <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract int MinRequiredPasswordLength <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract int MinRequiredNonAlphanumericCharacters <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> public abstract string PasswordStrengthRegularExpression <img src="http://www.cnblogs.com/Images/dot.gif">{ get; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> // Abstract methods<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract MembershipUser CreateUser (string username, <br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string password, string email, string passwordQuestion, <br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string passwordAnswer, bool isApproved, object providerUserKey,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> out MembershipCreateStatus status);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract bool ChangePasswordQuestionAndAnswer<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (string username, string password,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string newPasswordQuestion, string newPasswordAnswer);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract string GetPassword (string username,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string answer);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract bool ChangePassword (string username,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string oldPassword, string newPassword);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract string ResetPassword (string username,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string answer);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract void UpdateUser (MembershipUser user);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract bool ValidateUser (string username,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string password);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract bool UnlockUser (string userName);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract MembershipUser GetUser (object providerUserKey,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> bool userIsOnline);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract MembershipUser GetUser (string username,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> bool userIsOnline);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract string GetUserNameByEmail (string email);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract bool DeleteUser (string username,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> bool deleteAllRelatedData);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract MembershipUserCollection GetAllUsers<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (int pageIndex, int pageSize, out int totalRecords);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract int GetNumberOfUsersOnline ();<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract MembershipUserCollection FindUsersByName<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (string usernameToMatch, int pageIndex, int pageSize,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> out int totalRecords);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public abstract MembershipUserCollection FindUsersByEmail<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (string emailToMatch, int pageIndex, int pageSize,<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> out int totalRecords);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> // Virtual methods<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> protected virtual byte[] EncryptPassword (byte[] password);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> protected virtual byte[] DecryptPassword (byte[] encodedPassword);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> protected virtual void OnValidatingPassword<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (ValidatePasswordEventArgs e);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> // Events<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> public event MembershipValidatePasswordEventHandler<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ValidatingPassword;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">}<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top">  <p>SqlMembershipProvider类就是从这个类里继承下来的。  <p>接下来 我们使用SqlMembershipProvider类作为例子来进行比较细致的说明。  <p><b>SqlMembershipProvider</b>:  <p>SqlMembershipProvider是给Membership使用sql server数据库做的Provider,它使用数据库的存储过程来实现对数据的操作，这样SqlMembershipProvider可以经过很少的改动来 实现对其他数据库的支持。  <p><strong>1.Provider 初始化</strong>  <p>Provider初始化是在 SqlMembershipProvider.Initialize,它只运行一次，是在asp.net装载Provider时。  <p>a.初始化SqlMembershipProvider的各种属性 比如：EnablePasswordRetrieval 和 EnablePasswordReset，从相应的配置文件的配置属性中读入。  <p>b.对一些公共属性的值进行检查，当有错误的时候抛出异常，比如PasswordFormat值是”hashed”,而EnablePasswordRetrieval的值是true，就会有异常抛出。  <p>c.在配置里存在一些不被承认的属性时，也会抛出异常  <p>SqlMembershipProvider.Initialize还会从&lt;connectionStrings&gt;中读取数据库连接字符串，保存到一个私有的变量中，如果不能读到或者读取的连接字符串是错误的，也会抛出一个异常。  <p><strong>2.数据定义</strong>  <p>SqlMembershipProvider的Membership数据保存在数据库的aspnet_Membership表中  <p>aspnet_Membership 定义(msdn中取出)  <p>字段名  <p>字段类型  <p>表述  <p>ApplicationId  <p>uniqueidentifier  <p>Application ID，应用程序id  <p>UserId  <p>uniqueidentifier  <p>User ID,用户id  <p>Password  <p>nvarchar(128)  <p>密码，可以是加密 hash保存的  <p>PasswordFormat  <p>int  <p>Password format (0=Plaintext, 1=Hashed, 2=Encrypted)  <p>PasswordSalt  <p>nvarchar(128)  <p>Randomly generated 128-bit value used to salt password hashes; stored in base-64-encoded form  <p>MobilePIN  <p>nvarchar(16)  <p>User's mobile PIN (当前没有使用)  <p>Email  <p>nvarchar(256)  <p>email  <p>LoweredEmail  <p>nvarchar(256)  <p>小写email地址  <p>PasswordQuestion  <p>nvarchar(256)  <p>密码问题  <p>PasswordAnswer  <p>nvarchar(128)  <p>密码问题答案  <p>IsApproved  <p>bit  <p>1=Approved, 0=Not approved  <p>IsLockedOut  <p>bit  <p>1=Locked out, 0=Not locked out  <p>CreateDate  <p>datetime  <p>创建时间  <p>LastLoginDate  <p>datetime  <p>最后登录时间  <p>LastPasswordChangedDate  <p>datetime  <p>密码最后修改时间  <p>LastLockoutDate  <p>datetime  <p>最后登出的时间  <p>FailedPasswordAttemptCount  <p>int  <p>联系登录失败次数  <p>FailedPasswordAttempt-WindowStart  <p>datetime  <p>在FailedPasswordAttemptCount非零时，第一次登录失败的时间  <p>FailedPasswordAnswer-AttemptCount  <p>int  <p>回答密码问题联系失败的次数  <p>FailedPasswordAnswer-AttemptWindowStart  <p>datetime  <p>在FailedPasswordAnswer-AttemptCount非零时，第一次回答问题失败的时间  <p>Comment  <p>ntext  <p>扩展的文本  <p>这个表中的每一条记录代表一个用户，这个表还有两个外键，分别关联aspnet_Applications表和aspnet_Users表  <p>aspnet_Applications表  <p>字段名  <p>字段类型  <p>描述  <p>ApplicationId  <p>uniqueidentifier  <p>Application ID  <p>ApplicationName  <p>nvarchar(256)  <p>Application name  <p>LoweredApplicationName  <p>nvarchar(256)  <p>Application name (小写)  <p>Description  <p>nvarchar(256)  <p>Application 描述  <p>aspnet_Users 表  <p>字段名  <p>字段类型  <p>描述  <p>ApplicationId  <p>uniqueidentifier  <p>Application ID  <p>UserId  <p>uniqueidentifier  <p>用户ID  <p>UserName  <p>nvarchar(256)  <p>用户名  <p>LoweredUserName  <p>nvarchar(256)  <p>用户名 (小写)  <p>MobileAlias  <p>nvarchar(16)  <p>User's mobile alias (currently not used) 没有使用  <p>IsAnonymous  <p>bit  <p>1=Anonymous user, 0=Not an anonymous user  <p>LastActivityDate  <p>datetime  <p>用户最后一次活动时间  <p>一条完整的记录 aspnet_Membership和aspnet_Users都要存在。  <p><strong>3.数据访问</strong>  <p>SqlMembershipProvider是通过存储过程完成所有的数据库操作的，简单介绍一下这些存储过程  <p>SqlMembershipProvider存储过程  <p>名称  <p>描述  <p>aspnet_Membership_ChangePassword-QuestionAndAnswer  <p>修改密码，密码问题，密码问题答案  <p>aspnet_Membership_CreateUser  <p>增加一个membership用户 记录同时写入aspnet_Users 和aspnet_Membership 表, 如果需要，还要增加一条记录到aspnet_Applications表。  <p>aspnet_Membership_FindUsersByEmail  <p>通过email地址匹配查找用户的记录，同时还要一个application ID.  <p>aspnet_Membership_FindUsersByName  <p>通过用户名匹配查找用户的记录，同时还要一个application ID.  <p>aspnet_Membership_GetAllUsers  <p>得到所有用户记录，在一个下application ID.  <p>aspnet_Membership_GetNumberOfUsersOnline  <p>得到在线用户数 （通过用户最后活动时间字段（LastActivityDate）实现）  <p>aspnet_Membership_GetPassword  <p>得到一个用户的密码，通过密码问题的回答  <p>aspnet_Membership_GetPasswordWithFormat  <p>得到一个用户的密码。通过密码比较重新取得密码。  <p>aspnet_Membership_GetUserByEmail  <p>使用email和application id从aspnet_Membership中得到相应的记录  <p>aspnet_Membership_GetUserByName  <p>使用用户名和application id从aspnet_Membership中得到相应的记录  <p>aspnet_Membership_GetUserByUserId  <p>使用userid和application id从aspnet_Membership中得到相应的记录  <p>aspnet_Membership_ResetPassword  <p>重置用户密码通过回答密码问题  <p>aspnet_Membership_SetPassword  <p>设置一个密码  <p>aspnet_Membership_UnlockUser  <p>恢复用户登录的权限通过将IsLockedOut字段设置成0  <p>aspnet_Membership_UpdateUser  <p>更新用户的aspnet_users表的LastActivityDate,e-mail,注释，approved字段和aspnet_Membership表的最后登录时间。  <p>aspnet_Membership_UpdateUserInfo  <p>更新帐户锁定时间在aspnet_users和aspnet_Membership表，Used in conjunction with provider methods that track bad password and bad password-answer attempts.  <p>aspnet_Users_CreateUser  <p>调用aspnet_Membership_CreateUser增加一个到用户到aspnet_users表。  <p>aspnet_Users_DeleteUser  <p>删除一个用户 从aspnet_membership已经一些关联表里包括aspnet_users.  <p><strong>4.创建用户</strong>  <p>SqlMembershipProvider.CreateUser通过调用aspnet_Membership_CreateUser储存过程创建membership用户。SqlMembershipProvider.CreateUser在调用存储过程之前还会对用户的输入参数做一些校验，包括密码等。  <p>创建用户的流程  <p>a. 调用aspnet_Applications_CreateApplication,存储过程，转换一个ApplicationName成Application ID.如果在aspnet_Applications表中已经存在这个Application ID就返回存在Application ID,如果表中不存在，在aspnet_Applications表中新增加一条记录并返回这个Application ID.  <p>b. 调用aspnet_Users_CreateUser在aspnet_Users表中添加一条新记录  <p>c. 做一个验证对email地址和原来已经注册的用户。  <p>d. 使用当前的时间来更新一下aspnet_Users表的LastActityDate字段。  <p>e. 插入一条新记录到aspnet_Membership表。  <p>aspnet_Membership_CreateUser提供了所有的这些步骤，并使用事务来保证数据库更新的完整性。  <p><strong>5.删除用户</strong>  <p>程序通过调用Membership.DeleteUser来实现删除membership用户的功能。  <p>Membership.DeleteUser调用默认的membership提供者的DeleteUser的函数，而这个函数有两个输入值，一个是用户名 另外一个参数（deleteAllRelatedData）是bool值，这个bool值表示是否要删除这个用户的一些关联信息，包括role data, profile data和 Web Parts personalization data。  <p>DeleteUser还可以实现一个其他的功能就是 把用户名输入Request.AnonymousID,而参数deleteAllRelatedData设置为true,这样可以删除匿名用户在数据库的aspnet_Profile和aspnet_Users表中保存的记录。  <p><strong>6.验证membership用户</strong>  <p>程序通用调用Membership.ValidateUser来实现用户的验证，返回值是一个bool值，包括用户名密码是否正确。  <p>验证的流程  <p>a. 通过调用存储过程 aspnet_Membership_GetPasswordWithFormat得到用户的密码，如果是加密的返回的是加密的字串。  <p>b. 使用相同的加密码方法加密输入的密码。  <p>c. 比较两个密码。  <p>d. 如果密码匹配 会触发一个AuditMembershipAuthenticationSuccess的Web 事件，同时记录一个成功登陆的纪录，并返回true  <p>e. 如果密码不匹配，会触发一个 AuditMembershipAuthenticationFailure的web 事件，返回false，同时还会调用aspnet_Membership_UpdateUserInfo储存过程做记录,如果记录发现已经达到限制用户登录的条件，还会锁住此用户。  <p><strong>7.密码保护</strong>  <p>为了安全，密码保存在数据库中一般不用明文，SqlMembershipProvider提供了几种不同保存密码的方法，我们可以通过设置PasswordFormat属性来指定不同的保存方法。  <p>a. MembershipPasswordFormat.Clear使用明文保存  <p>b. MembershipPasswordFormat.Hashed 默认参数，会使用.Net框架的RNGCryptoServiceProvider类来对密码和密码问题进行Hash计算保存。  <p>c. MembershipPasswordFormat.Encrypted对密码和密码问题进行加密。SqlMembershipProvider使用的是对称密钥加密方法。加密的密钥保存在&lt;machineKey&gt;配置字段里  <p>为了增加一些额外的安全保护，SqlMembershipProvider还提供了MinRequiredPasswordLength，MinRequiredNonAlphanumericCharacters，PasswordStrengthRegularExpression三个属性来加强保护，根据字面意思应该是密码最小长度，最少特殊字符数，密码正则表达式。  <p><strong>8．帐户锁定</strong>  <p>为了抵御穷举密码 猜密码的攻击，SqlMembershipProvider提供了一个自动锁定用户的机制，当一个帐户在一段时间内连续登录失败超过一定次数后，这个用户将被锁定，SqlMembershipProvider的MaxInvalidPasswordAttempts和PasswordAttemptWindow属性，默认MaxInvalidPasswordAttempts=5次，PasswordAttemptWindow=10分钟。当数据表里的IsLockedOut=1时用户就被锁定了。  <p>对Membership Provider等Provider微软提供了源代码，但这些源代码和.Net框架里包含的是有区别的，主要是为了能让提供的源代码能让用户独立的编译和运行。<br>今天这篇文章写的太长，自己都有点晕了，有错误的地方多包涵。  <p>路漫漫其修远兮 吾将上下而求索  <p>&nbsp; <p><b>自定义</b><b>MembershipProvider</b>:<br>前面讲了<a href="http://www.cnblogs.com/dotLive/archive/2006/07/27/461243.html">内置的MembershipProvider,</a>这次 我们自己定义一个Provider。<br>首先我们确定一下保存数据使用ms 的access，好像土了点。<br>我还是用一个例子来说明：<br>启动 vs2005,创建一个Asp.net Web Site,名字就取一个NewMembershipProvider,在App_Data目录里 建一个Access数据库文件，命名：Members.mdb,创建一个表 名称：MemberShip  <p>字段名  <p>字段类型  <p>描述  <p>username  <p>文本8  <p>用户名 主键  <p>password  <p>文本8  <p>密码  <p>Email  <p>文本50  <p>邮件  <p>passwordQuestion  <p>文本50  <p>密码问题  <p>passwordAnswer  <p>文本50  <p>问题答案  <p>建完表之后，退出Access,备用:)。  <p>我们在网站生成的default.aspx上拖上两个login控件，首先拖上去一个CreateUserWizard控件，不用做什么修改，接着在CreateUserWizard控件下面放上一个LoginView控件，在LoginView的AnonymousTemplate 视图里拖上一个LoginStatus控件，并把LoginStatus控件的LogoutPageUrl设置为login.aspx，login系列控件的应用在我的<a href="http://www.cnblogs.com/dotLive/archive/2006/07/15/451441.html">membership(2)</a>中有比较详细的说明。<br>接着 我们建一个新的页面 login.aspx 在页面上拖上一个Login控件，准备工作完成。<br>在项目里新建一个类，命名为AccessMembershipProvider.cs，类的名字 AccessMembershipProvider.继承自MembershipProvider，vs2005会帮我们生成可以重载的函数，我们这里不会建立所有的新函数，，我们重载两个属性和两个函数  <p>首先建几个私有变量  <p>private string connStr;//保存数据库连接字符串<br>private bool _requiresQuestionAndAnswer;//是否需要问题和回答<br>private int _minRequiredPasswordLength;//最短密码长度  <p>需要重载的属性为：<br>MinRequiredPasswordLength 和RequiresQuestionAndAnswer<br>重载的函数<br>public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)<br>public override bool ValidateUser(string username, string password)<br>public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)  <p>下面列出部分的代码：  <p><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top"> public override int MinRequiredPasswordLength<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> get <img src="http://www.cnblogs.com/Images/dot.gif">{ return _minRequiredPasswordLength; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top"> public override bool RequiresQuestionAndAnswer<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> get<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> return _requiresQuestionAndAnswer;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top">  <p><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top"> public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> if (config["requiresQuestionAndAnswer"].ToLower() == "true")<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _requiresQuestionAndAnswer = true;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> else<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _requiresQuestionAndAnswer = false;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> int.TryParse (config["minRequiredPasswordLength"],out _minRequiredPasswordLength );<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connStr = config["connectionString"];<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> base.Initialize(name, config);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top"> public override bool ValidateUser(string username, string password)<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Data.OleDb.OleDbConnection conn=new System.Data.OleDb.OleDbConnection(connStr);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> try<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.Open();<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> string sql = "select * from Membership where username=@username and password=@password";<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Data.OleDb.OleDbCommand command = new System.Data.OleDb.OleDbCommand(sql, conn);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.Parameters.AddWithValue("@username", username);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; command.Parameters.AddWithValue("@password", password);<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Data.OleDb.OleDbDataReader reader = command.ExecuteReader();<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> if (reader.HasRows)<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.Close();<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> return true;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> else<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.Close();<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> return false;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> catch<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align="top"><img src="http://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif" align="top"> <img src="http://www.cnblogs.com/Images/dot.gif">{<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> if (conn.State == ConnectionState.Open)<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; conn.Close();<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"> return false;<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br><img src="http://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif" align="top"><br><img src="http://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align="top">&nbsp;&nbsp;&nbsp; }  <p><img src="http://www.cnblogs.com/Images/OutliningIndicators/None.gif" align="top"> public override MembershipUser Create