﻿<?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>博客园-幸福是一种心情、一种体会</title><link>http://www.cnblogs.com/lifework/</link><description>认真学习，虚心向高手们请教</description><language>zh-cn</language><lastBuildDate>Wed, 20 Aug 2008 23:18:24 GMT</lastBuildDate><pubDate>Wed, 20 Aug 2008 23:18:24 GMT</pubDate><ttl>60</ttl><item><title>link</title><link>http://www.cnblogs.com/lifework/archive/2005/10/02/248222.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Sun, 02 Oct 2005 15:07:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/10/02/248222.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/248222.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/10/02/248222.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/248222.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/248222.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 10pt">Visual Studio.NET 术语表 <A href="http://www.microsoft.com/china/msdn/vstudio/glossary/default.mspx">http://www.microsoft.com/china/msdn/vstudio/glossary/default.mspx</A></SPAN> <BR><SPAN style="FONT-SIZE: 10pt">PDC 2005 Sessions PPT <A href="http://commnet.microsoftpdc.com/content/downloads.aspx">http://commnet.microsoftpdc.com/content/downloads.aspx</A></SPAN><BR><FONT size=2><SPAN style="FONT-SIZE: 10pt"><FONT size=2>What's next in the LOB Program?</FONT> <A href="http://blogs.msdn.com/tomholl/archive/2005/10/02/LobFutures.aspx">http://blogs.msdn.com/tomholl/archive/2005/10/02/LobFutures.aspx</A></SPAN></FONT><img src ="http://www.cnblogs.com/lifework/aggbug/248222.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41802/" target="_blank">[新闻]微软Office 14即将展开Alpha测试</a>]]></description></item><item><title>共享自动生成DTO/DataAccess/BizObject的CodeSmith模板</title><link>http://www.cnblogs.com/lifework/archive/2005/09/03/229605.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Sat, 03 Sep 2005 14:06:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/09/03/229605.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/229605.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/09/03/229605.html#Feedback</comments><slash:comments>29</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/229605.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/229605.html</trackback:ping><description><![CDATA[<P><SPAN style="FONT-SIZE: 10pt">2005-9-13：更新了<A href="http://lifework.cnblogs.com/Files/lifework/BizObject.rar" target="">BizObject.cst</A>文件，如果原来文件中有此文件，就覆盖之。<BR>如果你有什么代码模板，可以与我们共享吗，我打算搞一个CodeSmith模板共享的团队，共享我们的模板(CodeSmith论坛上已经有很多了)。<BR>只要你点击一下按钮，DTO对象及其集合类、数据访问类、业务层类的部分代码自动生成，形成Model、DataAccess、Biz三个目录，并生成相应</SPAN><SPAN style="FONT-SIZE: 10pt">的各个类文件。<BR><A href="http://lifework.cnblogs.com/Files/lifework/MyTemplates.rar" target="">MyTemplates.rar</A><BR></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt">更新的<A href="http://lifework.cnblogs.com/Files/lifework/BizObject.rar" target="">BizObject.cst</A><BR>前提：<BR>1、建立数据库的各个表，最好以复数形式给表命名，字段名不要与表名相同。<BR>2、设定表的主键。<BR>3、设定表的外键。<BR>4、建立表的索引。</SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt">主要特点：<BR>1、支持一对多和多对一关系。<BR>2、支持like查询。</SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt">用法：<BR>1、完成"前提"中提到的各步。<BR>2、用AllStoredProcedures.cst生成所有存储过程<BR>3、用Model\Bulk.cst生成所有文件</SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt">不足：<BR>1、刚建立，还没来的及详细测试。<BR>2、可能会出现相同功能的存储过程，删掉其中一个就可以了，下一个版本计划修正这个问题。<BR>(里面虽然有制作自己的EnterpriseLibrary应用程序块的模板但没有内容，过段时间在给出)<BR></SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt">大家有什么好的架构，我可以帮着建立模板，同时希望对我建立的这个模板提出更多的意见。<BR><BR>这几天在看EDRA、GBI和<SPAN><SPAN class=header_chrome_page_title id=_ctl4_pageTitleLabel><FONT style="FONT-SIZE: 10pt" size=3>WS-I Basic Security Profile Sample Application</FONT></SPAN></SPAN>，有兴趣的共同学习。</SPAN></P><img src ="http://www.cnblogs.com/lifework/aggbug/229605.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41801/" target="_blank">[新闻]《星际争霸2》新图</a>]]></description></item><item><title>请教Entity/VO/DTO/Domain Object/Biz Object等名词的含义</title><link>http://www.cnblogs.com/lifework/archive/2005/09/03/229596.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Sat, 03 Sep 2005 13:59:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/09/03/229596.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/229596.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/09/03/229596.html#Feedback</comments><slash:comments>9</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/229596.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/229596.html</trackback:ping><description><![CDATA[<P><SPAN style="FONT-SIZE: 10pt">一、VO和DTO<BR>VO:&nbsp; Value Object<BR>DTO: Data Transfer Object<BR>个人理解VO和DTO是类似的东西，原则上VO和DTO只有Public Fields，主要用于进程之间数据传递的问题，VO和DTO不会传递到表示层，在业务层就会被吸收。但看到很多人在建立VO和DTO时，也</SPAN><SPAN style="FONT-SIZE: 10pt">含有Setter,Getter属性和一些其它的辅助方法，这也无可厚非，我自己也不能确定这对不对。望大家给出意见。<BR><BR>二、Entity和Domain Object<BR>Entity和Domain Object应该是类似的东西，我觉得这两者概念上可能与Biz Object(Business Object)有所不同，但我看到网上很多文档都把</SPAN><SPAN style="FONT-SIZE: 10pt">他们当成类似的东西。<BR>Entity和Domain Object除了有Setter,Getter属性外，还有仅仅属于自己的一些专有(special)方法,如CRUD及其他专有方法，和有一些</SPAN><SPAN style="FONT-SIZE: 10pt">Services接口，并不涉及Domain Object与Domain Object之间关系的一些方法。也就是说Domain Object负责数据持久化，这可以由其</SPAN><SPAN style="FONT-SIZE: 10pt">IDataServices接口来实现。Entity和Domain Object更强调具体是哪一个对象，或者说是实例化的Entity和Domain Object对象。<BR>如果用Domain Object来设计程序，不自觉地就会遵守一些重构策略(如Divergent Change、Shotgun Surgery、Switch Statements等，具体可</SPAN><SPAN style="FONT-SIZE: 10pt">以看Rickie的<A href="http://rickie.cnblogs.com/category/10512.html">blog</A>)，我们知道Domain Driven Design这本书出现在Refactoring这本书之后，看来也是Martin Fowler对Refactoring进一步总</SPAN><SPAN style="FONT-SIZE: 10pt">结、升华的结果。用Domain Object来设计程序，降低了类之间的耦合，可以不自觉地达到Refactoring结果。看来出现Refactoring后，Domain </SPAN><SPAN style="FONT-SIZE: 10pt">Object的出现只是时间的问题。</SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt">idior的<A href="http://idior.cnblogs.com/archive/2005/07/04/186086.aspx">O/R Mapping 基本概念</A>和<A href="http://bbs.dotnettools.org/activeubb/newsdetail.asp?id=2889&amp;page=1&amp;posts=&amp;Daysprune=0&amp;lp=5">一个困扰我长时间的问题</A>也谈到了Entity和Domain Object<BR></SPAN><SPAN style="FONT-SIZE: 10pt"><BR>三、Biz Object<BR>个人理解Biz Object更加专注于业务实现，主要强调业务类之间的关系。这也是它与Entity或Domain Object的不同之处。<BR>在具体的程序中有时还会有Manager类，用于管理Biz Object和作为Biz Object的Facade。</SPAN></P>
<P><SPAN style="FONT-SIZE: 10pt">我一直没有成型的架构，下一步抽时间看看<FONT style="FONT-SIZE: 10pt" size=3><A href="http://www.javaeye.com/viewtopic.php?t=9685"><FONT style="FONT-SIZE: 10pt" size=3>Domain Model 探索</FONT></A></FONT>和<A href="http://www.gotdotnet.com/codegallery/codegallery.aspx?id=9c29a963-594e-4e7a-9c45-576198df8058">EDRA</A>，我更觉得架构不用通用，可以根据一些典型的业务建立合适的架构，更希望大家能给</SPAN><SPAN style="FONT-SIZE: 10pt">出好的架构。</SPAN></P><img src ="http://www.cnblogs.com/lifework/aggbug/229596.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41801/" target="_blank">[新闻]《星际争霸2》新图</a>]]></description></item><item><title>Enterprise LibraryV1.0-缓存应用程序块</title><link>http://www.cnblogs.com/lifework/archive/2005/08/23/221309.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Tue, 23 Aug 2005 13:33:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/08/23/221309.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/221309.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/08/23/221309.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/221309.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/221309.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 10pt">
<P>缓存应用程序块<BR>1、简介<BR>我们知道，在Asp.net中有个System.Web.Caching名称空间，但在Windows Form中却没有System.Windows.Forms.Caching名称空间，当我们在WinForm程序中使用缓存时，往往会引用System.Web.Caching名称空间，用其缓存机制来解决缓存问题。但用起来毕竟感到有些别扭，缓存应用程序块应运而生，但其更大的作用在于Smart Client程序中缓存本地数据到数据库或其他存储位置。缓存应用程序块比起System.Web.Caching名称空间来有更好的一致性和扩展性，同时能满足WinForm应用程序、基于WinForm的Smart Client、Web等应用程序的缓存机制。如果在web程序中使用缓存，System.Web.Caching足以应付，关键是开发者要有意识的去使用，并灵活运用它。如果想使程序的缓存机制具有一致的策略、更加简洁的调用方法、更好的扩展性，还是要用缓存应用程序块。<BR>在日常编程中，我们往往会由这样的需求：<BR>1、 我们不断重复地使用相同的一组数据，这组数据来源于持久化设备，读取时要花费一定的资源和时间，我们希望获取数据之后缓存于本地，下一次读取时直接从本地内存中读取。<BR>2、 我们希望离线的使用一组数据，这组数据从数据源(网络、数据库、Webservices等)得到后要持久化到本地，下一次读取时避免重新从数据源读取。<BR>3、 当应用程序的客户端数目加大、数据吞吐量变大时，程序还能保持很好的性能，即程序有可扩展性。<BR>4、 我们缓存的数据要及时更新，以便与数据源保持一致。<BR>缓存应用程序块就是基于此目的设计的，其主要特点是：<BR>1、 可以应用于各类应用程序。如WinForm应用程序、Smart Client、Web应用程序、Enterprise Services等。也可以应用到程序架构的不同Layer上。<BR>2、 灵活可变的缓存Storage选择。支持isolated storage、Database、内存，而且可以无缝的切换。<BR>3、 修改缓存策略时，不用修改源代码。<BR>4、 支持过期后，自动删除策略。支持基于时间的过期设置，也支持基于文件变化的过期设置。如果用基于时间的过期策略，则可以是绝对时间、相对时间、自定义格式的时间等。<BR>2、使用步骤<BR>①用配置控制台添加缓存应用程序块。<BR>添加步骤同其他应用程序块。<BR>当使用基于内存的缓存策略时，有几个参数要设置，其中ExpirationPollFrequencyInSecond是指过期轮询周期，就是多长时间就检查一下有哪些缓存项过期了，由后台独立进程执行。这个参数很关键，如果时间设置的太短，就会使这个后台进程不断执行，占用大量CPU时间，如果太长，又会浪费内存，要找个不长不短的，因此推荐用性能计数器监测一下，选一个适中的参数。<BR>添加完毕后，默认支持基于内存的缓存，你还可以添加多个缓存管理器Cache Manager，以使用不同的缓存策略。<BR>②写代码添加缓存项<BR>// 最简单的添加缓存项：这是最简单的一种，我们也最常用，直接建立默认的Cache Manager<BR>CacheManager cacheManager = CacheFactory.GetCacheManager();<BR>cacheManager.Add("key", myObject);<BR>// 创建指定的Cache Manager<BR>CacheManager cacheManager = CacheFactory.GetCacheManager("Custom Manager");<BR>cacheManager.Add("key", myObject);<BR>// 基于绝对过期时间的缓存<BR>// 过期是指当到了指定时间后，系统会将此缓存项清除掉，如果你再次访问的话，会重建。<BR>CacheManager isolatedCacheManager = CacheFactory.GetCacheManager("Isolated Cache Manager");<BR>DateTime refreshTime = new DateTime(2005, 7, 28, 17, 10, 10);</P>
<P>AbsoluteTime expireTime = new AbsoluteTime(refreshTime);</P>
<P>isolatedCacheManager.Add("myKey",dataSet, CacheItemPriority.Normal, null, expireTime);<BR>// 注意上面这个例句是Isolated Storage基于隔离缓存的，可以看出Client代码的写法与用内存、数据库来缓存的代码完全一样，这保证了当你的缓存策略发生改变时，Client代码不用作任何修改。<BR>// 相对过期时间的缓存<BR>TimeSpan timeSpan = new TimeSpan(0, 10, 0);<BR>SlidingTime expireTime = new SlidingTime(timeSpan);</P>
<P>cacheManager.Add("myKey1", cachedObject, CacheItemPriority.Normal,&nbsp; null, expireTime);<BR>CacheManager有只有两个重载的Add方法，其Sign如下：<BR>public void Add(string key, object value);<BR>public void Add(string key, object value, CacheItemPriority scavengingPriority, ICacheItemRefreshAction refreshAction, params ICacheItemExpiration[] expirations);<BR>// 自定义格式的过期设置<BR>// 格式为：&lt;Minute&gt; &lt;Hour&gt; &lt;Day of month&gt; &lt;Month&gt; &lt;Day of week&gt;<BR>// 如：3 * * * *表示每小时的第3分钟就过期<BR>//&nbsp;&nbsp;&nbsp;&nbsp; * 1 * * *表示每天凌晨1点的每分钟<BR>//&nbsp;&nbsp;&nbsp;&nbsp; 15 2 * * *表示每天2点15都会过期<BR>ExtendedFormatTime expireTime = new ExtendedFormatTime("0 1 * * 5");<BR>cacheManager.Add("key", cachedObject, CacheItemPriority.Normal, null, expireTime);</P>
<P>// 依赖于文件的过期机制<BR>FileDependency&nbsp;expireNotice = new FileDependency("文件名");<BR>productsCache.Add("key", cachedObject, CacheItemPriority.Normal, null, expireNotice);<BR>缓存应用程序块还提供了过期提醒机制，不过这要实现ICacheItemRefreshAction接口，用于过期时执行一些自定义的操作，比如提醒。<BR>③写代码读取缓存项<BR>这个与我们以往System.Web.Caching的用法基本一致。<BR>public BizEntry ReadBizEntryByID(string bizEntryKey)<BR>{<BR>&nbsp;BizEntry bizEntry = (Product)cache.GetData(bizEntryKey);<BR>&nbsp;if (bizEntry == null)<BR>&nbsp;{<BR>&nbsp;&nbsp;// 这里如果被执行说明缓存缓存中没有<BR>// 此处的正常代码逻辑是先建立一个BizEntry对象<BR>// 再将这个BizEntry对象放到缓存中，当然也可以不放进去<BR>&nbsp;}<BR>}<BR>这里用的是CacheManager的GetData()方法：public object GetData(string key);<BR>注意这里要进行强制类型转换。<BR>BizEntry bizEntry = (Product)cache.GetData(bizEntryKey);<BR>或者<BR>BizEntry bizEntry = cache.GetData(bizEntryKey) as BizEntry;<BR></P></SPAN><img src ="http://www.cnblogs.com/lifework/aggbug/221309.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41800/" target="_blank">[新闻]Google放出Android开发路线图</a>]]></description></item><item><title>Enterprise LibraryV1.0-异常处理应用程序块使用说明 </title><link>http://www.cnblogs.com/lifework/archive/2005/08/06/208880.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Sat, 06 Aug 2005 06:35:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/08/06/208880.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/208880.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/08/06/208880.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/208880.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/208880.html</trackback:ping><description><![CDATA[<FONT face="Times New Roman" color=#000000 size=3><SPAN style="FONT-SIZE: 8pt">
<P><FONT style="FONT-SIZE: 10pt" face="Times New Roman" color=#000000 size=3><SPAN style="FONT-SIZE: 10pt"><FONT face="Times New Roman" color=#000000 size=3><BR></FONT></SPAN>其他几个应用程序块的使用说明连接如下：<BR><A href="http://lifework.cnblogs.com/archive/2005/08/03/207035.html">Enterprise LibraryV1.0-概述</A>&nbsp;&nbsp;<BR><A href="http://lifework.cnblogs.com/archive/2005/08/03/207039.html">Enterprise LibraryV1.0-配置应用程序块<BR></A><A href="http://lifework.cnblogs.com/archive/2005/08/04/207705.html">Enterprise LibraryV1.0-安全应用程序块使用说明</A><BR><A href="http://lifework.cnblogs.com/archive/2005/08/04/207715.html">Enterprise LibraryV1.0-数据应用程序块</A><BR><A href="http://lifework.cnblogs.com/archive/2005/08/05/208159.html">Enterprise LibraryV1.0-日志应用程序块使用说明</A><BR><A href="http://lifework.cnblogs.com/archive/2005/08/05/208166.html">Enterprise LibraryV1.0-加密应用程序块使用说明</A><BR><BR>1、简介</FONT></P>
<P><FONT face="Times New Roman" color=#000000 size=3><SPAN style="FONT-SIZE: 10pt"><FONT style="FONT-SIZE: 10pt" face="Times New Roman" color=#000000 size=3>只要是编写代码，我们就会面对异常处理的问题。系统预定义异常由于肩负着提示、调试等很多功能，因此当异常发生时，会有非常详细的异常信息。当我们编写商用程序时，如果程序出现运行时异常，显示给用户的信息都要求是user-friendly的，而不是系统级，特别是系统预定义异常会deliver一些敏感信息，如果被别有用心的人获取，是非常危险的，所以当异常出现时，我们要屏蔽掉这些敏感信息。对异常的处理一般有四种方式：<BR> 根据源异常建立一个新异常，重新将此新异常抛出。<BR> 取代源异常，抛出一个别的异常。<BR> Log异常，用于查看、排错等。<BR> 不做任何处理，只是将传播路径截断。<BR>虽然AOP是异常处理最佳方案，但技术上远未成熟，我们都还是用传统的方式来处理异常。对应这几种情况，我们一般都会这样来处理：<BR><SPAN style="FONT-SIZE: 10pt"><SPAN style="FONT-SIZE: 12pt"></P><SPAN style="FONT-SIZE: 10pt">
<P>&nbsp;1) //&nbsp; 提示用户的处理方式，出错后提示用户<BR>try<BR>{<BR>&nbsp;&nbsp;&nbsp; // 正常执行语句;<BR>}<BR>catch(Exception ex)<BR>{<BR>&nbsp;&nbsp;&nbsp; String formattedInfo = FormatException(ex);<BR>&nbsp;&nbsp;&nbsp; Logging.Log(formattedInfo); // 或Logger.Log(e);<BR>Throw new MyException("出错了。");<BR>// 或者直接提示客户<BR>//MessageBox.Show(errorMsg, "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Stop);<BR>//lblError.Text = errorMsg;<BR>}<BR>2) // logging，出错后作记录<BR>catch(SomeException e)<BR>{<BR>&nbsp;&nbsp;&nbsp; string formattedInfo = FormatException(e);<BR>&nbsp;&nbsp;&nbsp; Logger.Log(e);</P>
<P><BR>&nbsp;&nbsp;&nbsp; throw e;<BR>}</P>
<P>3) //replace，抛出一个新异常，屏蔽掉敏感信息<BR>catch(SomeException e)<BR>{<BR>&nbsp;&nbsp;&nbsp; CustomException myException = new CustomException(&#8220;XXXXXXXXXXXXXXXXXXXXXX&#8221;);</P>
<P>&nbsp;&nbsp;&nbsp; throw myException;<BR>}<BR>4) //wrapping 包封原来的异常，添加Context信息后，重新抛出<BR>catch(SomeException e)<BR>{<BR>&nbsp;&nbsp;&nbsp; CustomException myException = new CustomException(e);</P>
<P>&nbsp;&nbsp;&nbsp; throw myException;<BR>}<BR>5) //propagating 记录后，继续传播之<BR>catch(SomeException e)<BR>{<BR>&nbsp;&nbsp;&nbsp; // Exception handling code to be done before propagating<BR>&nbsp;&nbsp;&nbsp; string formattedInfo = FormatException(e);<BR>&nbsp;&nbsp;&nbsp; Logger.Log(e);</P>
<P>&nbsp;&nbsp;&nbsp; throw e;<BR>}<BR>一般来说，象上面这样的处理方式都非常不灵活，有极大的代码重复，当要求发生变化时，可能源代码要做大量的机械修改。<BR>异常处理应用程序块在充分满足上述异常处理要求的基础上，使我们在不同项目之间、同一个项目的不同开发人员之间建立起了统一的异常处理策略。当需求发生改变时，我们可以用配置工具进行修改配置文件，不用去修改源码，从而不用重新编译、发布程序。<BR>我们知道，在.Net里一个异常发生时，系统会抛出一个Exception类型或其子代的对象，习惯上我们要对这个对象进行一些处理，以排除错误或提示用户，常见的处理方式有提示用户、包装后重新抛出、用另一个异常对象取而代之、记录等。EHAB就是根据这样的要求而设计的，但她并不负责具体如何处理，而是做了大量基础工作，使对异常的处理非常简单，同时统一了处理策略，增加了灵活性。<BR>下面我们以常用的异常处理方式来说明异常处理应用程序块的用法，来看看使用EHAB是多么简单<BR>2、用配置控制台配置异常处理应用程序块<BR>①添加异常处理应用程序块<BR>添加异常处理应用程序块后，就要添加一个或多个Exception Policy。一个异常处理应用程序块可以维护一个或多个Exception Policy，异常处理策略往往代表了对异常的处理方式，常见的策略有包封、取代、继续传播、处理并继续、记录等等，当然异常处理策略如何分类、有哪些异常处理策略，都是你自己说了算，只是取一个比较有意义的名字就行了。<BR>每种Exception Policy可以管理多种类型的异常，而每种类型的Exception又可以有多个Handler(异常处理器，在Exception类的继承层次结构中，可以针对每种类型的Exception设置一个Handler，这样一个子代Exception就可以有多个Handler)。<BR>②添加Exception Policy<BR>添加几个Policy，适当修改Policy的名称，如Wrap Policy、Replace Policy、Log Policy、Propagate Policy、GlobalPolicy等。<BR>③给每个Policy添加几个Exception<BR>当添加一个Exception类型时，我们不用自己键入，向导会提示我们选一个，这个异常类型可以是系统预定义的，也可以是来自某个程序集的，还可以是我们自己定义的。<BR>我们按自己的需要给每种Policy添加好Exception。然后针对每种类型的Exception设置处理活动PostHandlingAction。PostHandlingAction有三种分别是None、NotifyRethrow、ThrowNewException。<BR>None表示此异常被内部处理掉了，调用者不用再处理了。<BR>NotifyRethrow表示Application.ThreadException事件的处理器将重新抛出异常。<BR>ThrowNewException表示这类异常你可以用一个新的异常类包封或取代之。<BR>④设置Exception的处理器handler<BR>每个Exception一般都会设置一个或多个handler，当然也可以不设。Replace Handler要设置好其取代的异常类型，Wrap Handler也要置好其包封的异常类型，Log Handler要置好LogCategory。<BR>⑤设置完毕，保存。</P>
<P>3、写代码<BR>String policy = &#8220;Wrap Policy&#8221;;<BR>Try<BR>{<BR>// 。。。。。。。<BR>}<BR>catch (Exception ex)<BR>{<BR>&nbsp;&nbsp;&nbsp; if (ExceptionPolicy.HandleException(ex, policy))<BR>&nbsp;&nbsp;&nbsp; throw;<BR>}<BR>其他类型Policy的代码与此类似，但异常的PostHandlingAction设为NotifyRethrow时，我们要将定义一个异常处理handler，并挂接到Application.ThreadException事件上。我们可以看到，对异常的不同处理方式其代码是一致的，而不象开始时我们列出的代码，不同的处理方式代码是不同的。</SPAN></SPAN></SPAN></FONT></SPAN><SPAN style="FONT-SIZE: 12pt"></P></SPAN></FONT></SPAN></FONT><img src ="http://www.cnblogs.com/lifework/aggbug/208880.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41799/" target="_blank">[新闻]番茄花园作者被警方逮捕</a>]]></description></item><item><title>Enterprise LibraryV1.0-加密应用程序块使用说明 </title><link>http://www.cnblogs.com/lifework/archive/2005/08/05/208166.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Fri, 05 Aug 2005 04:40:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/08/05/208166.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/208166.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/08/05/208166.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/208166.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/208166.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 10pt">
<P>&nbsp; <A name=_Toc111008293><SPAN lang=EN-US><B><SPAN>加密应用程序块</SPAN></B></A><B></B></P>
<P><A name=_Toc111008294><SPAN lang=EN-US>1</SPAN></A><SPAN><SPAN>、概述</SPAN></P>
<P><SPAN>当在不安全信道上传递一些敏感数据时，要对这些数据采取保护措施，即便是被人截获也难以从中得到所携带的信息，当我们保存一些敏感数据到物理设备时。也需要先将这些数据加密。</SPAN></P>
<P><SPAN>在对数据加密时，对于不同的场景采用的加密方式是不同的，一般来说常用的数据加密方式有三种。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&#178;<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><B><SPAN>私钥加密</SPAN></B><SPAN>：私钥加密算法使用单个私钥来加密和解密数据。由于具有密钥的任意一方都可以使用该密钥解密数据，因此必须保护密钥不被未经授权的代理得到。私钥加密又称为对称加密或单钥加密，因为同一密钥既用于加密又用于解密。私钥加密算法非常快（与公钥算法相比），特别适用于对较大的数据流执行加密转换。一般情况下，对正式数据的传送中，一般先用公钥传送对称密钥，再用这个对称密钥加密要传送的数据。<SPAN>私钥加密的关键是如何保护对称密钥</SPAN>。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&#178;<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><B><SPAN>公钥加密</SPAN></B><SPAN>：公钥加密使用一个必须对未经授权的用户保密的私钥和一个可以对任何人公开的公钥。公钥和私钥都在数学上相关联；用公钥加密的数据只能用私钥解密，而用私钥签名的数据只能用公钥验证。公钥可以提供给任何人；公钥用于对要发送到私钥持有者的数据进行加密。两个密钥对于通信会话都是唯一的。公钥加密算法也称为不对称算法，原因是需要用一个密钥加密数据而需要用另一个密钥来解密数据。它只可以加密少量数据。公钥加密一般用于交换私钥加密的私钥、对摘要进行签名等。</SPAN></P>
<P><B><SPAN>单向哈希加密</SPAN></B><SPAN>：哈希算法将任意长度的二进制值映射为固定长度的较小二进制值，这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母，随后的哈希计算都将产生不同的值。要找到散列为同一个值的两个不同的输入，在计算上是不可能的。注意当用哈希算法对消息进行散列后是不能解密的，只能对同样的消息用同样的哈希算法加密，对照加密后的值是否一致，从而判断消息是否被篡改。哈希加密主要用于密码验证、身份验证、数据完整性等方面。比如数字签名的基本原理如下：<BR><BR>&nbsp; <IMG src="http://www.cnblogs.com/images/cnblogs_com/lifework/crypto.GIF" border=0></P>
<P><SPAN>可以看出，数字签名不是一种具体的技术实现，它是基于以上各种加密技术组合的一种数据验证解决方案。</SPAN></P>
<P><SPAN>我们在配置应用程序块中已说明如何将配置数据加密，其他应用程序块的加密方式也相同，一般数据和配置应用程序块的配置数据我们会加密。比较理想的做法是用配置控制台将配置文件加密时，用<SPAN lang=EN-US>DPAPI Provider </SPAN>，这样我们就不用保存对称密钥了，系统会自动管理，这样数据加密就完全透明了，无需我们任何干预。</SPAN></P>
<P><A name=_Toc111008295><SPAN lang=EN-US>2</SPAN></A><SPAN><SPAN>、使用步骤</SPAN></P>
<P><A name=_Toc111008296><SPAN>⑴用配置控制台添加加密应用程序块</SPAN></A></P>
<P><SPAN>跟前面的配置方法一致，添加加密应用程序块后，会有两个节点<SPAN lang=EN-US>Hash Provider</SPAN>和<SPAN lang=EN-US>Symmetric Provider</SPAN>，没有提供<SPAN lang=EN-US>Asymmetric Provider</SPAN>，分别提供哈希加密和对称加密的功能。这里已对称加密的设置为例来说明，在<SPAN lang=EN-US>Symmetric Provider</SPAN>节点上点右键，<SPAN lang=EN-US>New-&gt;Symmetric Algorithm Provider</SPAN>，选<SPAN lang=EN-US>RijndaelManaged</SPAN>，<SPAN lang=EN-US>Rijndael</SPAN>是美国<SPAN lang=EN-US>AES</SPAN>选定的加密标准，<SPAN lang=EN-US>DES(</SPAN>原来的加密标准<SPAN lang=EN-US>)</SPAN>会逐渐被放弃，<SPAN lang=EN-US>.Net Framework</SPAN>中内置的<SPAN lang=EN-US>Rijndael</SPAN>算法的<SPAN lang=EN-US>secure key</SPAN>为<SPAN lang=EN-US>32byte</SPAN>，<SPAN lang=EN-US>IV</SPAN>为<SPAN lang=EN-US>16byte</SPAN>，比其他算法强度都高，而且非常高效，是首先得对称加密算法。选定<SPAN lang=EN-US>RijndaelManaged</SPAN>后，我们再选<SPAN lang=EN-US>Generate</SPAN>创建一个对称密钥，然后确认就可以了。</SPAN></P>
<P><A name=_Toc111008297><SPAN>⑵写代码</SPAN></A></P>
<P><SPAN>我们一般用其<SPAN lang=EN-US>Fa&#231;ade</SPAN>类</SPAN><SPAN lang=EN-US>Cryptographer</SPAN><SPAN>来写代码，</SPAN></P>
<P><SPAN lang=EN-US>// </SPAN><SPAN>哈希算法的四个方法原型如下：</SPAN></P><PRE><SPAN lang=EN-US><SPAN style="FONT-SIZE: 8pt"><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=19">byte</A>[] <B>CreateHash</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=20">string</A> hashInstance, <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=21">byte</A>[] plaintext);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=22">string</A> <B>CreateHash</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=23">string</A> hashInstance, <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=24">string</A> plaintext);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>&nbsp;</SPAN></PRE><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.Boolean href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=1">bool</A> <B>CompareHash</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=2">string</A> hashInstance, <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=3">byte</A>[] plaintext, <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=4">byte</A>[] hashedText);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.Boolean href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=5">bool</A> <B>CompareHash</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=6">string</A> hashInstance, <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=7">string</A> plaintext, <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=8">string</A> hashedText);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>// </SPAN><SPAN>对称加解密的四个方法原型如下：</SPAN></PRE><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=47">string</A> <B>EncryptSymmetric</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=48">string</A> symmetricInstance, <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=49">string</A> plaintext);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=50">byte</A>[] <B>EncryptSymmetric</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=51">string</A> symmetricInstance, <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=52">byte</A>[] plaintext);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>&nbsp;</SPAN></PRE><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=33">string</A> <B>DecryptSymmetric</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=34">string</A> symmetricInstance, <A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=35">string</A> ciphertextBase64);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>public</SPAN><SPAN lang=EN-US> <SPAN>static</SPAN> <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=36">byte</A>[] <B>DecryptSymmetric</B>(<A title=System.String href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=37">string</A> symmetricInstance, <A title=System.Byte href="http://www.aisto.com/roeder/dotnet/Default.aspx?Object=38">byte</A>[] ciphertext);<BR></SPAN></PRE><PRE><SPAN lang=EN-US>&nbsp;</SPAN></PRE><P><SPAN lang=EN-US></SPAN></P></SPAN></SPAN><SPAN>①对称加密代码<BR></SPAN><SPAN lang=EN-US>//</SPAN><SPAN>加密<BR></SPAN><SPAN lang=EN-US>private</SPAN><SPAN lang=EN-US> <SPAN>const</SPAN> <SPAN>string</SPAN> symmProvider = "SymmProvider";<BR></SPAN><SPAN lang=EN-US>string</SPAN><SPAN lang=EN-US> cipherText = Cryptographer.EncryptSymmetric(symmProvider, </SPAN><SPAN>要加密的文本<SPAN lang=EN-US>);<BR></SPAN><SPAN lang=EN-US>//</SPAN><SPAN>解密<BR></SPAN><SPAN lang=EN-US>string</SPAN><SPAN lang=EN-US> plainText = Cryptographer.DecryptSymmetric(symmProvider, </SPAN><SPAN>密码文本<SPAN lang=EN-US>);<BR></SPAN><SPAN lang=EN-US>// </SPAN><SPAN>或者用下面的代码</SPAN></PRE>
<P><SPAN lang=EN-US>//</SPAN><SPAN>加密<BR></SPAN><SPAN lang=EN-US>String[]</SPAN><SPAN lang=EN-US> cipherText = Cryptographer.EncryptSymmetric(symmProvider, Encoding.Unicode.GetBytes(password));<BR></SPAN><SPAN lang=EN-US>//</SPAN><SPAN>解密<BR></SPAN><SPAN lang=EN-US>string[]</SPAN><SPAN lang=EN-US> plainText = Cryptographer.DecryptSymmetric(symmProvider, Encoding.Unicode.GetBytes(password));</SPAN></P>
<P><SPAN>注意这里的<SPAN lang=EN-US>symmProvider </SPAN>是指标示具体的加密算法节点名称，如<SPAN lang=EN-US>RijndaelManaged</SPAN>，而不是节点&#8220;<SPAN lang=EN-US>Symmetric Providers</SPAN>&#8221;的名称。上面两种加解密代码，到底用那种要看你具体应用情况。</SPAN></P>
<P><SPAN>②哈希算法代码<BR></SPAN><SPAN lang=EN-US>private</SPAN><SPAN lang=EN-US> <SPAN>const</SPAN> <SPAN>string</SPAN> hashProvider = "PasswordHasher";<BR></SPAN><SPAN lang=EN-US>string</SPAN><SPAN lang=EN-US> hashPassword = Cryptographer.CreateHash(hashProvider, originalPassword);//</SPAN><SPAN>生成摘要<BR></SPAN><SPAN lang=EN-US>bool </SPAN><SPAN lang=EN-US>isPass = Cryptographer.CompareHash(hashProvider, originalPassword, hashPassword);//</SPAN><SPAN>比较是否与原来的相同<BR></SPAN><SPAN lang=EN-US>// </SPAN><SPAN>或者<BR></SPAN><SPAN lang=EN-US>String[]</SPAN><SPAN lang=EN-US> hashPassword = Cryptographer.CreateHash(hashProvider, Encoding.Unicode.GetBytes(password));//</SPAN><SPAN>生成摘要<BR></SPAN><SPAN lang=EN-US>bool </SPAN><SPAN lang=EN-US>isPass = Cryptographer.CompareHash(hashProvider, Encoding.Unicode.GetBytes(password), hashPassword);//</SPAN><SPAN>比较是否与原来的相同</SPAN></P>
<P><SPAN>还有就是用哈希加密时一定要用<SPAN lang=EN-US>Salted hash</SPAN>，这样强度更高。</SPAN></SPAN></SPAN></SPAN></SPAN></P></SPAN></SPAN></SPAN></SPAN></SPAN><img src ="http://www.cnblogs.com/lifework/aggbug/208166.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41792/" target="_blank">[新闻]WordPress和Movable Type影响社交网站的未来</a>]]></description></item><item><title>Enterprise LibraryV1.0-日志应用程序块使用说明 </title><link>http://www.cnblogs.com/lifework/archive/2005/08/05/208159.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Fri, 05 Aug 2005 04:33:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/08/05/208159.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/208159.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/08/05/208159.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/208159.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/208159.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 10pt">&nbsp; 
<P><A name=_Toc111008270><B><SPAN lang=EN-US><SPAN>一、<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></B><B><SPAN>日志应用程序块</SPAN></B></A><B></B></P>
<P><A name=_Toc111008271><SPAN lang=EN-US>1</SPAN></A><SPAN><SPAN>、<B>概述</B></SPAN></P>
<P><SPAN>一般来说，日志模块是一个项目必不可少的部分。最常用的场景就是将对业务数据的操作记录下来，用于以后的安全审计。比如进行了何种操作，修改的数据是什么等等。其他的应用有：</SPAN></P>
<P><SPAN lang=EN-US><SPAN>------<SPAN>&nbsp;</SPAN></SPAN><SPAN>跟踪程序的运行，将运行中的一些信息记录下来，这对于程序开发时期和试用时期非常重要，我们可以根据这些信息对程序加以改进。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>------</SPAN><SPAN>将日志信息写到不同的地方，比如写系统日志中、写到数据库中，甚至发送</SPAN><SPAN lang=EN-US>email</SPAN><SPAN>等。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>------</SPAN><SPAN>可定制的日志输出格式。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>------</SPAN><SPAN>将一条日志同时发往不同的地方。</SPAN></P>
<P><SPAN lang=EN-US><SPAN>------<SPAN>&nbsp;</SPAN></SPAN><SPAN>对于日志我们要设置其优先级，以决定是否记录，记录的位置、格式等等。</SPAN></P>
<P><SPAN>当然还有其他一些对日志的要求。在这里关键的是当我们对日志的处理策略发生该变时，我们往往要修改原代码，这不但要重新编译程序，而且会带来很大的工作量。日志应用程序块的出现改变了这种情况，我们可以在运行时改变配置文件，从而改变记录日志的种类、优先级、目的地等等。</SPAN></P>
<P><SPAN>在此我们以将日志写到数据库为例，简要介绍其用法</SPAN></P>
<P><A name=_Toc111008272><SPAN lang=EN-US>2</SPAN></A><SPAN><SPAN>、<B>配置步骤</B></SPAN></P>
<P><A name=_Toc111008273><B><SPAN>⑴添加</SPAN></B></A><SPAN><B><SPAN>日志应用程序块</SPAN></B></SPAN><B></B></P>
<P><SPAN>添加后，我们可以看到日志和监控应用程序块有两个配置节，一个为<SPAN lang=EN-US>Client Settings</SPAN>，一个为<SPAN lang=EN-US>Distributor Settings</SPAN>。实际在物理上，系统用两个配置文件来分别保存着两个设置（<SPAN lang=EN-US>loggingconfiguration.config</SPAN>和<SPAN lang=EN-US>loggingdistributorconfiguration.config</SPAN>）。</SPAN></P>
<P><B><SPAN lang=EN-US>Client Settings</SPAN></B><SPAN>用于设置发布策略，这里有两种，即<SPAN lang=EN-US>In Process</SPAN>和<SPAN lang=EN-US>MSMQ</SPAN>。进程内发布是一种同步发布方式，我们比较常用，<SPAN lang=EN-US>MSMQ</SPAN>是异步发布方式，用于将日志发布到<SPAN lang=EN-US>MSMQ</SPAN>对列，由<SPAN lang=EN-US>MSMQ</SPAN>依次处理。<B><SPAN lang=EN-US>Distributor Setting</SPAN></B><SPAN lang=EN-US>s</SPAN>用于设置各种类别的日志的发布<B>目的地和格式</B>，日志可以按不同的分类方式进行分类，这个我们自己掌握，比如可以将日志分为操作的安全审计日志、登录日志、跟踪日志等等，每种日志又具有持久化格式和发布目的地<SPAN lang=EN-US>(</SPAN>也叫接收器<SPAN lang=EN-US>)</SPAN>，在这里我们可以设置多种发布格式和接收器，在设置每种类别的发布格式和接收器时，可以从中选择。除了默认的接收器<SPAN lang=EN-US>Event Log Sink</SPAN>和<SPAN lang=EN-US>Flat File Sink</SPAN>外，我们这里还设置了一个<SPAN lang=EN-US>Database Sink</SPAN>。</SPAN></P>
<P><A name=_Toc111008274><B><SPAN>⑵添加日志<SPAN lang=EN-US>Sink</SPAN></B></A><B></B></P>
<P><SPAN>也就是添加接收器类型，我们添加一个<SPAN lang=EN-US>Database Sink</SPAN>，设置好其数据库实例名称和存储日志的<SPAN lang=EN-US>SP</SPAN>名。</SPAN></P>
<P><A name=_Toc111008275><B><SPAN>⑶添加日志类别</SPAN></B></A><B></B></P>
<P><SPAN>根据我们自己应用程序的要求添加类别，如</SPAN><SPAN>安全审计日志、登录日志、跟踪日志等等，对于每个类别取一个比较有意义的名字。</SPAN></P>
<P><A name=_Toc111008276><SPAN>⑷添加发布目的地</SPAN></A></P>
<P><SPAN>对于添加的每个日志类别，添加发布目的地，一个日志类别可以添加多个发布目的地，设置每个发布目的地的日志格式和接收器。</SPAN></P>
<P></P>
<P><SPAN lang=EN-US>&nbsp;<IMG src="http://www.cnblogs.com/images/cnblogs_com/lifework/logDistributor.GIF" border=0></SPAN></P>
<P><A name=_Toc111008277><SPAN>⑸设置完毕，保存退出。</SPAN></A></P>
<P><A name=_Toc111008278><SPAN lang=EN-US>3</SPAN></A><SPAN><SPAN>、<B>写代码</B></SPAN></P>
<P><SPAN>记录日志的代码如下：</SPAN></P>
<P><SPAN lang=EN-US>Logger.Write("logMessage", "LogCatagory", 1);</SPAN></P>
<P><SPAN lang=EN-US>//</SPAN><SPAN>或者</SPAN></P>
<P align=left><SPAN lang=EN-US>LogEntry logEntry = <SPAN>new</SPAN> LogEntry();</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp; </SPAN><SPAN>&nbsp;</SPAN>logEntry.Category = " LogCatagory ";</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp; </SPAN><SPAN>&nbsp;</SPAN>logEntry.Priority = 1;</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>logEntry.Severity = Severity.Error;</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>logEntry.Message = logMessage;</SPAN></P>
<P><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp; </SPAN><SPAN>&nbsp;</SPAN>Logger.Write(logEntry);</SPAN></P><SPAN>日志记录的内容非常详细，除了有日志消息外，还有很多附加信息，如日志的类别、优先级、机器名称、应用程序域、时间贴等等。我们可以动态的改变日志的优先级和发布目的地，而不用重新编译、部署程序。</SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN><img src ="http://www.cnblogs.com/lifework/aggbug/208159.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41792/" target="_blank">[新闻]WordPress和Movable Type影响社交网站的未来</a>]]></description></item><item><title>Enterprise LibraryV1.0-数据应用程序块</title><link>http://www.cnblogs.com/lifework/archive/2005/08/04/207715.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Thu, 04 Aug 2005 14:30:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/08/04/207715.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/207715.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/08/04/207715.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/207715.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/207715.html</trackback:ping><description><![CDATA[<SPAN style="FONT-SIZE: 10pt">
<P><BR>1、简介<BR>数据应用程序块把我们从大量的机械的数据库访问代码中解放出来，同时给我们带来了在多个数据库甚至多个不同类型的数据库的中无缝切换的可能。<BR>数据应用程序块的类继承层次非常简单，这里就不做详细介绍了(向看此文的朋友问个问题：为什么Vs.net2003的类视图窗口中很多类都不显示？)。<BR>我们在我前面的配置应用程序块中提到过，要读取我们的自定义配置数据，首先要写一个配置数据类，数据应用程序块也不例外，这个类就是DatabaseSettings，其结构如下图：<IMG src="http://www.cnblogs.com/images/cnblogs_com/lifework/DataConfigurationData.gif" border=0></P>
<P>从这个图里，我们可以看到DatabaseSettings主要包括3个集合类，DatabaseTypeData说明了配置的数据库类型，ConnectionStringData指明了每种数据库类型的连接串，InstanceData指明了当前的数据库实例。<BR>2、使用说明<BR>使用时分两步：先用配置控制台添加数据应用程序块，再在程序中写代码。<BR>⑴用配置控制台添加数据应用程序块<BR>①添加数据应用程序块，之后是分别设置应用程序要访问的数据库类型、每个类型的数据库即连接串和当前的默认数据库。<BR>②添加应用程序要访问的数据库类型(Database Types)，如SQL Server、Oracle、Sybase、Access等<BR>③设置每个数据库的连接串。添加上相应的连接参数。<BR>④设定默认的数据库及其连接串。<BR>⑤设置完毕，保存。<BR>⑵写数据访问代码<BR>数据应用程序块支持我们常见的大部分功能：执行SQL文本，执行存储过程、启用事务、离线处理、批量更新等等，下面我们分别举例说明。<BR>①执行SQL语句<BR>Database db = DatabaseFactory.CreateDatabase();<BR>string sql = "SELECT * FROM [AddressList]";<BR>DBCommandWrapper cmdWrapper = db.GetSqlStringCommandWrapper(sql);<BR>return db.ExecuteDataSet(cmdWrapper);<BR>②执行存储过程<BR>Database db = DatabaseFactory.CreateDatabase();</P>
<P>string sqlCommand = "GetUserById";<BR>DBCommandWrapper cmd = db.GetStoredProcCommandWrapper(sqlCommand);<BR>cmd.AddInParameter("@UserId", DbType.Int32, 1);<BR>Literal1.Text = (string)db.ExecuteScalar(cmd);<BR>③启用事务<BR>Database db = DatabaseFactory.CreateDatabase();<BR>string sqlCommand = "CreditAccount";<BR>DBCommandWrapper creditCommandWrapper = db.GetStoredProcCommandWrapper(sqlCommand);<BR>sqlCommand = "DebitAccount";<BR>DBCommandWrapper debitCommandWrapper = db.GetStoredProcCommandWrapper(sqlCommand);<BR>using (IDbConnection connection = db.GetConnection())//<BR>{<BR>&nbsp;connection.Open();<BR>&nbsp;IDbTransaction transaction = connection.BeginTransaction();<BR>&nbsp;try<BR>&nbsp;{<BR>&nbsp;&nbsp;db.ExecuteNonQuery(creditCommandWrapper, transaction);<BR>&nbsp;&nbsp;db.ExecuteNonQuery(debitCommandWrapper, transaction);<BR>&nbsp;&nbsp;transaction.Commit();<BR>&nbsp;&nbsp;result = true;<BR>&nbsp;}<BR>&nbsp;catch<BR>&nbsp;{<BR>&nbsp;&nbsp;transaction.Rollback();<BR>&nbsp;}<BR>&nbsp;connection.Close();<BR>&nbsp;return result;<BR>}<BR>④批量更新<BR>Database db = DatabaseFactory.CreateDatabase();<BR>DataSet productsDataSet = new DataSet();<BR>string sqlCommand = "Select ProductID, ProductName, CategoryID, UnitPrice, LastUpdate From Products";<BR>DBCommandWrapper dbCommandWrapper = db.GetSqlStringCommandWrapper(sqlCommand);<BR>string productsTable = "Products";</P>
<P>db.LoadDataSet(dbCommandWrapper, productsDataSet, productsTable);</P>
<P>DataTable table = productsDataSet.Tables[productsTable];<BR>DataRow addedRow = table.Rows.Add(new object[] {DBNull.Value, "New product", 11, 25});<BR>table.Rows[0]["ProductName"] = "Modified product";</P>
<P>DBCommandWrapper insertCommandWrapper = db.GetStoredProcCommandWrapper("AddProduct");<BR>insertCommandWrapper.AddInParameter("@ProductName", DbType.String, "ProductName", DataRowVersion.Current);<BR>insertCommandWrapper.AddInParameter("@CategoryID", DbType.Int32, "CategoryID", DataRowVersion.Current);<BR>insertCommandWrapper.AddInParameter("@UnitPrice", DbType.Currency, "UnitPrice", DataRowVersion.Current);</P>
<P>DBCommandWrapper deleteCommandWrapper = db.GetStoredProcCommandWrapper("DeleteProduct");<BR>deleteCommandWrapper.AddInParameter("@ProductID", DbType.Int32, "ProductID", DataRowVersion.Current);</P>
<P>DBCommandWrapper updateCommandWrapper = db.GetStoredProcCommandWrapper("UpdateProduct");<BR>updateCommandWrapper.AddInParameter("@ProductID", DbType.Int32, "ProductID", DataRowVersion.Current);<BR>updateCommandWrapper.AddInParameter("@ProductName", DbType.String, "ProductName", DataRowVersion.Current);<BR>updateCommandWrapper.AddInParameter("@LastUpdate", DbType.DateTime, "LastUpdate", DataRowVersion.Current);</P>
<P>int rowsAffected = db.UpdateDataSet(productsDataSet, "Products", insertCommandWrapper, updateCommandWrapper,<BR>deleteCommandWrapper, UpdateBehavior.Standard);<BR>这里的代码来源于Quick Start,这里需要注意的就是DataRowVersion.Current，批量更新时根据具体情况选择其值。同时ExecuteScalar、UpdateDataSet等方法重载好几个不同的方法原型，您可以根据具体情况选择。<BR>⑶注意的问题<BR>这里需要注意的就是在执行存储过程时，有两个获取IDbCommand对象的方法：<BR>public abstract DBCommandWrapper GetStoredProcCommandWrapper(string storedProcedureName);<BR>public abstract DBCommandWrapper GetStoredProcCommandWrapper(string storedProcedureName, params object[] parameterValues);<BR>如果你要缓存参数，就用第二个方法，不缓存参数就用第一个方法(不知我说的对不对？)，因为在准备IDbCommand对象时，有下面的语句，<BR>if (command.IsFurtherPreparationNeeded())<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parameterCache.FillParameters(command, ParameterToken);<BR>}<BR>这样，在用第二个方法时，会从缓存中取参数，当你多次调用同一存储过程时，可以考虑用用第二个方法，当然，如果有必要可以缓存执行的结果，性能更佳，这将用到缓存应用程序块。</P>
<P>还需要提一句，就是对于保存在配置文件中的数据库信息，我们也是可以加密的，方法与配置应用程序块的加密方式一致。可以看我关于配置应用程序块的使用方法说明。每个应用程序块的配置数据都是可以加密保存的。</P>
<P>有人已经制作了使用数据应用程序块的模板，比较不错：<A href="http://cstemplates.sourceforge.net/">http://cstemplates.sourceforge.net</A></P></SPAN><img src ="http://www.cnblogs.com/lifework/aggbug/207715.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41791/" target="_blank">[新闻]谷歌平台手机获准上市</a>]]></description></item><item><title>Enterprise LibraryV1.0-安全应用程序块使用说明</title><link>http://www.cnblogs.com/lifework/archive/2005/08/04/207705.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Thu, 04 Aug 2005 14:17:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/08/04/207705.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/207705.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/08/04/207705.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/207705.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/207705.html</trackback:ping><description><![CDATA[<P><SPAN style="FONT-SIZE: 10pt">目前的安全应用程序块实际上是解决权限管理问题的。<BR>我不再对其源码进行解释了，有兴趣的朋友可以一起沟通。</SPAN><BR><BR><BR><SPAN style="FONT-SIZE: 10pt"></P>
<P>1、权限管理概述<BR>在我们的项目中基本上都会面对权限管理的问题，大家各有办法，虽然基本上能够满足要求，但与满意的解决这个问题都是有差距的，往往是某些需求没有很好的满足，勉强能用，不灵活，难以扩展。这里举个例子，比如在一个销售部门里有很多员工，他们都属于Employee权限组(Role)，都有浏览订单、添加订单的权限，但没有审批订单的权限，有一个员工Johnny（张三）也属于Employee权限组，但要求他只能浏览订单浏览，没有其他权限，我们在代码中一般这样写：<BR>If (user.Role in Employee &amp;&amp; user.UserName == &#8220;Johnny&#8221;)<BR>{<BR>&nbsp;&nbsp;// 只能浏览<BR>}<BR>Else if(user.Role in Employee)<BR>{<BR>&nbsp;&nbsp;// 可以浏览、添加<BR>}（代码一）<SPAN lang=EN-US style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">(</SPAN><SPAN style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">这个是伪代码，只是表明含义<SPAN lang=EN-US>)</SPAN></SPAN><BR>这时，需求发生了变化：Sunny原来具有Employee权限组的所有权限，但现在要求她只能浏览订单的，我们不得不求修改源代码，重新编译、重新发布程序(当然，就这个问题而言，再建一个Role让Johnny和Sunny属于这个Role即可)。更困难的是，如果原来是根据已有角色写代码的，当添加新的角色时，我们就不得不重新修改原来的编码逻辑，重新设计业务流程，重新编写源代码。这样的代码维护起来非常困难，也极不灵活，甚至不能完全满足用户需求。一种解决办法是，在判断是否属于某些角色时，这些角色来源于配置文件或数据库，而不是硬编码到程序中，同时对于权限判断中的个别的用户名也源于配置文件或数据库，这就带来了灵活性，实际上很多情况下我们都是这样做。但是这样做我们还是免不了要用这样带代码来写逻辑: if(user.Role in Roles &amp;&amp; user.UserName in Users) &#8230;&#8230;这样做还是未解决权限判断的灵活性。因为当我执行一项操作(如浏览、添加、删除、审批)时，期待的权限判断应该是这样的：<BR>If(user.IsAuthorized)<BR>{<BR>&nbsp;&nbsp;// 执行相应功能<BR>}(代码二)<BR>而不是象(代码一)那样，复杂而不灵活。实际上，当用户user登录以后，其对此项操作的IsAuthorized为true还是false就已经确定了，如果用代码二，当需求发生变化，登录用户的角色、权限改变时，我们的客户端代码不做任何修改，也就不用重新编译、重新发布了，只修改服务器端就行了。更加方便的是，如果我们将授权的规则写到配置文件或数据库，服务器端也不用修改，只是修改配置数据就行了。这样当需求发生变化时，我们的代码不做任何修改，修改配置就能应付变化了。这也是Martin Flower的plugin模式的具体应用。<BR>实际上，象日志记录、异常处理、权限管理等都不属于业务逻辑，而是一些功能项的附属物，其最新解决办法是基于AOP的编程，但AOP远未成熟(再说我也就是只知道AOP这个名词，具体深层次的实现也不甚了了)，难以商用，再说也可能还没有成熟就被其他技术所取代了，但对于权限管理的具体实现方式而言，有多种，其中基于角色的管理(RBAC)是最有效的，在2004年2月，RBAC被美国国家标准委员会(ANSI)和IT国际标准委员会(INCITS)接纳为ANSI INCITS 359-2004标准。其实，整个.Net Framework的权限设置就是根据RBAC设计的(猜的)。安全应用程序块在RBAC的应用上作了进一步努力，使其更加实用，安全应用程序块的权限设计思想并不仅限于.Net，它是一个权限管理解决方案，用其他语言（如java、vc）做项目时，完全可以参照它的设计思想。<BR>现在的安全应用程序块主要完成权限管理的功能，我们在使用它之前要明白一个基本的处理过程，就是用户提供一个证书(一般是账号和密码的组合)，由验证系统验证是否合法，如果合法，系统就提供一个通过验证的标志(IIdentity)，这就完成了验证(实际上就是常见的登录过程)。根据用户这个IIdentity我们可以得到一个IPrincipal对象(实现IPrincipal接口的类的对象)，即用户安全上下文，它包含了用户标识(IIdentity)和用户所属的角色(Roles)。同时对于每一项操作而言，我们都规定了其可以操作的角色(这里用一个Rule表示对一项功能的操作规则),当要执行这项操作之前，我们就拿此操作的Rule与IPrincipal进行比照，看看此IPrincipal是否满足这个Rule，如果满足说明有操作的权限，反之则没有。这样就达到了验证与授权的目的。<BR>2、安全应用程序块简介<BR>安全应用程序块提供一系列对象来完成上述认证、授权、角色管理的功能，同时还提供个性化设置功能。我们知道，完成认证、授权、角色管理等功能的方式有很多种，比如授权，我们可以根据win2k3提供的AzMan来进行授权，也可以根据配置文件中保存的Rule来授权，还可以根据数据库中保存的Rule来授权，又比如角色管理，我们可以根据数据库中保存的角色进行角色管理，也可以根据windows域里的角色来进行角色管理。具体用选哪一种，要根据项目的具体情况来选择，通过运用工厂模式，我们可以在不同功能提供者之间进行无缝切换，甚至在一个应用程序里综合运用多个功能提供者(当然，对于一项功能来说只能用一种功能提供者)。下面分别介绍。<BR>①认证<BR>提供认证机制的具体对象可以有多种，认证提供者工厂类根据当前的设置创建一个具体的认证提供者对象。这个认证提供者对象根据用户证书执行认证功能，从而产生一个IIdentity接口对象。用代码简单描述如下：<BR>// 创建一个用户证书<BR>NamePasswordCredential&nbsp; credentials = new NamePasswordCredential(username, password);<BR>// 由认证提供者工厂创建一个认证提供者<BR>IAuthenticationProvider authenProvider = AuthenticationFactory.GetAuthenticationProvider();</P>
<P>IIdentity identity;<BR>// 认证提供者根据证书执行认证功能，从而得到一个用户是否通过验证的标志<BR>authenticated = authenProvider.Authenticate(credentials, out identity);</P>
<P>②角色管理<BR>当得到一个IIdentity实例(实现IIdentity接口的类的实例)后，我们就可以根据这个IIdentity实例得到用户的IPrincipal，这个IPrincipal是根据物理存储的数据得到的，一般是根据用户帐号去查询配置文件或数据库中这个账号的角色，最后实例化一个IPrincipal对象(实现IPrincipal接口的类的对象)。<BR>// 角色管理工厂根据配置数据创建一个角色管理提供者<BR>IRolesProvider rolesProvider = RolesFactory.GetRolesProvider();<BR>// 角色管理器根据identity获取角色，并返回一个IPrincipal对象<BR>IPrincipal principal = rolesProvider.GetRoles(identity);<BR>// 将IPrincipal赋值给当前上下文。注意winform和webform的不同用法，不一定必须是这样用，也可以有别的用法<BR>// 比如在webform里可以建立一个FormsAuthenticationTicket对象来处理<BR>Thread.CurrentPrincipal = principal; // HttpContext.Current.User = principal;<BR>(注意上述代码只是说明问题的代码，不一定能运行)</P>
<P>③授权<BR>到底当前用户能不能执行某项操作是由授权管理部分确定的。判断是否有权执行某项操作之前，我们还要做一个基础性工作，就是对于每一项操作我们都要定义好哪些角色或用户能够执行，哪些角色或用户不能执行这样一个规则Rule，如何定义这个规则呢？Configuration Console提供了一个图形化的设计工具，很好设置，当然，你也可以自己手动创建这写的规则，也可以不用Configuration Console提供了一个图形化的设计工具，在自己的工程里实现一个。实际上，这个工具在我们大部分的项目中都能找到类似的实现，简单点说就是设置某项功能可以由哪些角色(和用户)执行。制定好Rule后具体保存到哪里由你自己决定，Rule工厂类屏蔽了这样的细节，对你的代码没有任何影响，不过好像大部分人都保存到数据库里(虽然Enterprise Library1.1没有实现将Rule保存到数据库里，但在Enterprise Library Communication可以找到)。当定义好Rule后，操作就和Rule一一对应了，你就可以查看IPrincipal是否有权执行某项操作了。代码非常简单：<BR>//授权<BR>public static bool Authorized(string rule)<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool authorized = false;<BR>&nbsp;&nbsp;// 授权工厂根据配置创建一个授权提供者<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IAuthorizationProvider ruleProvider = AuthorizationFactory.GetAuthorizationProvider();<BR>&nbsp;&nbsp;// 查看当前用户是否有权限执行某项操作，这里的参数rule就是代表了某项操作的名字<BR>authorized = ruleProvider.Authorize(Thread.CurrentPrincipal, rule);// asp.net:HttpContext.Current User<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return authorized;<BR>}</P>
<P>④个性化设置<BR>Personalization也许对你的程序在实现业务逻辑是帮助不大，但如果运用适当它会使你的程序更眩，更人性化，更具有亲和力，从而迅速得到用户认可。其实Personalization设置有点像配置应用程序块，你先写一个Profile类来定义好你的个性化信息，这个类是可串行化的，当然如果你要对不同角色或用户应用不同的个性化设置，就要保存多条这样的记录。根据不同的角色或用户来提取其个性化Profile对象。与配置应用程序块一样，也是先设置才能读取。<BR>设置：<BR>ProfileInfo _profile = new ProfileInfo();<BR>// 设置个性化信息<BR>&#8230;&#8230;.<BR>// 由个性化配置信息提供者的工厂类根据配置数据建立一个个性化配置信息提供者<BR>IProfileProvider profileProvider = profileProvider = ProfileFactory.GetProfileProvider();<BR>// 将配置信息保存<BR>profileProvider.SetProfile(Thread.CurrentPrincipal.Identity, _profile);<BR>读取：<BR>IProfileProvider profileProvider = ProfileFactory.GetProfileProvider();<BR>ProfileInfo _profile = (ProfileInfo) profileProvider.GetProfile(Thread.CurrentPrincipal.Identity);<BR>然后应用这个profile，设置个性化信息。<BR>3、安全应用程序块使用步骤<BR>下面介绍如何在我们的项目中使用安全应用程序块，其基本使用步骤是：<BR>在数据库中建立相关的表和存储过程。<BR>添加一些用户、角色、用户所属角色的数据。<BR>设置程序每一项功能的操作规则。<BR>用配置控制台设置安全应用程序块。<BR>写操作代码。<BR>在这里，一些大家都知道如何做的细节问题我们将不再赘述，如添加引用、添加using语句等等。在具体说明如何使用之前，我们先assume一个scenario，我们以先前ASP.NET Starter Kit里的一个程序IssueTracker为例来说明其基本使用步骤(如果不了解IssueTracker，请先了解一下)。<BR>Users设置为Johnny、Bob、Sunny、Alice、Tom五人。<BR>Roles设置为Administrator、Project Manager、Consultant、Developer、Guest、Architect、QA Tester七个角色。<BR>Administrator具有全部功能，但主要起着维护系统的作用，实际并不参与系统的Business Logic。<BR>Project Manager具有添加、修改、删除项目的权限，还有添加、修改、删除Issue的authority。<BR>Developer具有添加、修改Issue的authority，无删除权限。<BR>Guest仅有浏览的authority。<BR>我们要先将这些假想数据，想办法保存进数据库(当然，也可以是其它persistent storage)，下面我们详述使用步骤。</P>
<P><A name=_Toc111003643><B><SPAN>⑴在数据库中建立基础数据</SPAN></B></A><B></B></P>
<P><SPAN>①建立几个表，如<SPAN lang=EN-US>Users</SPAN>、<SPAN lang=EN-US>Roles</SPAN>、<SPAN lang=EN-US>UserRoles</SPAN>等，</SPAN></P>
<P><SPAN>②建立几个存储过程，如<SPAN lang=EN-US>GetAllUsers</SPAN>、<SPAN lang=EN-US>GetAllRoles</SPAN>等，如果操作的<SPAN lang=EN-US>Rule</SPAN>保存到数据库中，你也要添加<SPAN lang=EN-US>Rules</SPAN>表和<SPAN lang=EN-US>GetAllRules</SPAN>存储过程。表的名字可以随便起，但存储过程的名字，已经写死在了代码中，最好不要修改了。</SPAN></P>
<P><A name=_Toc111003644><B><SPAN>⑵添加数据</SPAN></B></A><B></B></P>
<P><SPAN>按照你的商业逻辑在<SPAN lang=EN-US>Users</SPAN>、<SPAN lang=EN-US>Roles</SPAN>、<SPAN lang=EN-US>UserRoles</SPAN>表中保存一些数据，为了达到这个功能我们一般会自己做一些界面，让用户可以增删改用户和角色，同时制作设置用户角色的界面。<SPAN lang=EN-US>EL</SPAN>也提供了这方面的一个工具：安全配置控制台，来达到维护这方面数据的目的。</SPAN></P>
<P><SPAN>（一些数据库设置方面的细节问题可以通过仔细查看<SPAN lang=EN-US>SecurityDatabase.sql</SPAN>和<SPAN lang=EN-US>UserRoleManager.cs</SPAN>这两个文件来解决。）</SPAN></P>
<P><SPAN>在这里我们用<SPAN lang=EN-US>EL</SPAN>提供的安全配置控制台来设置数据，在具体项目中你也许用自己定制的<?xml:namespace prefix = st1 /><st1:RTX w:st="on"><SPAN lang=EN-US>web</SPAN></st1:RTX><SPAN lang=EN-US>form</SPAN>或<SPAN lang=EN-US>winform</SPAN>界面来进行这项工作。</SPAN></P>
<P align=center></P>
<P><SPAN lang=EN-US></SPAN></P>
<P><SPAN><IMG src="http://www.cnblogs.com/images/cnblogs_com/lifework/securityData.gif" border=0><BR>当我们选择好一个数据库后，点连接按钮，就会连接到指定的数据库，这里是<SPAN lang=EN-US>IssueTracker</SPAN>，我们可以看到现在数据库里的<SPAN lang=EN-US>Users</SPAN>和<SPAN lang=EN-US>Roles</SPAN>什么没有。可以在左边栏中添加角色，在右边栏中添加用户，并为用户分配角色。按照我们上面的<SPAN lang=EN-US>assume</SPAN>我们设置好数据。</SPAN></P>
<P><A name=_Toc111003645><B><SPAN>⑶设置权限</SPAN></B></A><B></B></P>
<P><SPAN>设置完用户、角色以及用户所述角色之后，并没有结束，还要设置我们的系统有哪些操作，哪些角色和用户能执行这些操作，即操作权限。<IMG src="http://www.cnblogs.com/images/cnblogs_com/lifework/securitySetRule.png" border=0></SPAN></P>
<P></P>
<P><SPAN>在这里我们设置了几项操作，并决定让哪些角色和用户可执行，请注意<SPAN lang=EN-US>Rule</SPAN>的写法要规范，同时工具还提供了校验功能。</SPAN></P>
<P><A name=_Toc111003646><B><SPAN>⑷用配置控制台设置安全应用程序块</SPAN></B></A><B></B></P>
<P><SPAN>设置完基础数据后，我们就可以正式开工了，先进行配置。</SPAN></P>
<P><SPAN>①</SPAN><SPAN>打开配置控制台，添加<SPAN lang=EN-US>Security AB</SPAN>，并开设置各项的值。这里我们以设置<SPAN lang=EN-US>Authentication(</SPAN>登录管理<SPAN lang=EN-US>)</SPAN>、<SPAN lang=EN-US>Authoriztion(</SPAN>操作权限管理<SPAN lang=EN-US>)</SPAN>、<SPAN lang=EN-US>Roles(</SPAN>角色管理<SPAN lang=EN-US>)</SPAN>三项为例来说明，这也是我们最常用的。</SPAN></P>
<P><SPAN>②如果没有添加数据库应用程序块和加密应用程序块，就添加二者，并设置好各个属性，加密应用程序块要添加一个<SPAN lang=EN-US>HashAlgorithm Provider</SPAN>。</SPAN></P>
<P><SPAN>③在</SPAN><SPAN lang=EN-US>Authentication</SPAN><SPAN>节点上</SPAN><SPAN>添加一个<SPAN lang=EN-US>Database </SPAN><SPAN lang=EN-US>AuthenticationProvider</SPAN><SPAN>。设置好其<SPAN lang=EN-US>Database</SPAN>和<SPAN lang=EN-US>HashProvider</SPAN>。</SPAN></P>
<P><SPAN>④在<SPAN lang=EN-US>Authorization</SPAN><SPAN>节点上</SPAN><SPAN>添加一个<SPAN lang=EN-US>Database Authorization</SPAN><SPAN lang=EN-US> Provider</SPAN><SPAN>。设置好其<SPAN lang=EN-US>Database</SPAN>。</SPAN><SPAN> </SPAN></P>
<P></P>
<P><SPAN lang=EN-US>&nbsp;<IMG src="http://www.cnblogs.com/images/cnblogs_com/lifework/securitySetAuth.gif" border=0></SPAN></P>
<P><SPAN>⑤设置角色的<SPAN lang=EN-US>Provider</SPAN>，与设置</SPAN><SPAN lang=EN-US>Authentication</SPAN><SPAN>和</SPAN><SPAN lang=EN-US>Authorization</SPAN><SPAN>类似，不再赘述。</SPAN></P>
<P><SPAN>⑥</SPAN><SPAN>设置<SPAN lang=EN-US>Security AB</SPAN>的各个<SPAN lang=EN-US>Provider</SPAN>，选定<SPAN lang=EN-US>Security Application Block</SPAN>节点，设置好各个<SPAN lang=EN-US>Default Provider</SPAN>。</SPAN></P>
<P><SPAN>⑦保存设置，完毕。</SPAN></P>
<P><A name=_Toc111003647><B><SPAN>⑸写验证的代码</SPAN></B></A><B></B></P>
<P align=left><SPAN>验证的代码逻辑为：</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>l<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN>创建一个证书。</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>l<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN>由设定的验证机制根据这个证书建立标识。</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>l<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN>再由此标识建立主体对象，即安全上下文。</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>l<SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN><SPAN>根据安全上下文建立票据，并放到<SPAN lang=EN-US>Cookies</SPAN>中。</SPAN></P>
<P align=left><SPAN>当客户端不启用<SPAN lang=EN-US>Cookies</SPAN>时，虽然<SPAN lang=EN-US>Session ID</SPAN>会放到来回<SPAN lang=EN-US>trip</SPAN>的<SPAN lang=EN-US>URL</SPAN>中，但<SPAN lang=EN-US>Forms</SPAN>身份验证就不行了，所以当你选择了<SPAN lang=EN-US>Forms</SPAN>身份验证，就意味着客户端<SPAN lang=EN-US>Browser</SPAN>必须启用<SPAN lang=EN-US>Cookie,</SPAN>否则你不能使用<SPAN lang=EN-US>Forms</SPAN>身份验证。不过这是<SPAN lang=EN-US>ASP.NET</SPAN>说了算的，听说新版本中，<SPAN lang=EN-US>Forms</SPAN>身份验证可以选择以 <SPAN lang=EN-US>Cookieless </SPAN>方式工作。</SPAN></P>
<P align=left><SPAN>验证代码的最终目的是建立一个<SPAN lang=EN-US>IPrincipal</SPAN>对象和保存一个检索此<SPAN lang=EN-US>Principal</SPAN>对象的票据<SPAN lang=EN-US>(token</SPAN>或<SPAN lang=EN-US>ticket)</SPAN>，并使票据可恢复。</SPAN></P>
<P align=left><SPAN lang=EN-US>bool</SPAN><SPAN lang=EN-US> authenticated = <SPAN>false</SPAN>;</SPAN></P>
<P align=left><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P align=left><SPAN lang=EN-US>// </SPAN><SPAN>验证登录用户时添加的代码</SPAN></P>
<P align=left><SPAN lang=EN-US>NamePasswordCredential<SPAN>&nbsp; </SPAN>credentials = <SPAN>new</SPAN> NamePasswordCredential(txtUserName.Text, txtPassword.Text);</SPAN></P>
<P align=left><SPAN lang=EN-US>IAuthenticationProvider authenProvider = AuthenticationFactory.GetAuthenticationProvider();</SPAN></P>
<P align=left><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P align=left><SPAN lang=EN-US>IIdentity identity;</SPAN></P>
<P align=left><SPAN lang=EN-US>authenticated = authenProvider.Authenticate(credentials, <SPAN>out</SPAN> identity);</SPAN></P>
<P align=left><SPAN lang=EN-US>if</SPAN><SPAN lang=EN-US>(!authenticated)</SPAN></P>
<P align=left><SPAN lang=EN-US>{</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>lblPassword.Text = "";</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>return</SPAN>;</SPAN></P>
<P align=left><SPAN lang=EN-US>}</SPAN></P>
<P align=left><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P align=left><SPAN lang=EN-US>// </SPAN><SPAN>获取当前用户的角色列表</SPAN></P>
<P align=left><SPAN lang=EN-US>IRolesProvider rolesProvider = RolesFactory.GetRolesProvider();</SPAN></P>
<P align=left><SPAN lang=EN-US>IPrincipal principal = rolesProvider.GetRoles(identity);</SPAN></P>
<P align=left><SPAN lang=EN-US>// </SPAN><SPAN>放到缓存中</SPAN></P>
<P align=left><SPAN lang=EN-US>ISecurityCacheProvider securityCache = SecurityCacheFactory.GetSecurityCacheProvider();</SPAN></P>
<P align=left><SPAN lang=EN-US>IToken token = securityCache.SavePrincipal(principal);</SPAN></P>
<P align=left><SPAN lang=EN-US>// </SPAN><SPAN>建立票据</SPAN></P>
<P align=left><SPAN lang=EN-US>FormsAuthenticationTicket ticket = <SPAN>new</SPAN> FormsAuthenticationTicket(</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>1,</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>txtUserName.Text,</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>System.DateTime.Now,</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>System.DateTime.Now.AddMinutes(30),</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>false</SPAN>,</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>token.Value,// </SPAN><SPAN>仅仅是<SPAN lang=EN-US>principal</SPAN>的<SPAN lang=EN-US>key</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>FormsAuthentication.FormsCookiePath);</SPAN></P>
<P align=left><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P align=left><SPAN lang=EN-US>// </SPAN><SPAN>加密登录票据<SPAN lang=EN-US>.</SPAN></P>
<P align=left><SPAN lang=EN-US>string</SPAN><SPAN lang=EN-US> encryptedTicket = FormsAuthentication.Encrypt(ticket);</SPAN></P>
<P align=left><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P align=left><SPAN lang=EN-US>// </SPAN><SPAN>创建<SPAN lang=EN-US>Cookie</SPAN>，添加到<SPAN lang=EN-US>HttpCookieCollection.</SPAN></P>
<P align=left><SPAN lang=EN-US>Response.Cookies.Add(<SPAN>new</SPAN> HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket));</SPAN></P>
<P align=left><SPAN lang=EN-US>// </SPAN><SPAN>重新回到原来的<SPAN lang=EN-US>URL.</SPAN></P>
<P><SPAN lang=EN-US>Response.Redirect(FormsAuthentication.GetRedirectUrl(txtUserName.Text,<SPAN>true</SPAN>));</SPAN></P>
<P><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P><A name=_Toc111003648><B><SPAN>⑹写执行权限判断的代码</SPAN></B></A><B></B></P>
<P><SPAN>在验证时我们已经建立了</SPAN><SPAN lang=EN-US>IPrincipal</SPAN><SPAN>对象</SPAN><SPAN>，当判断权限时，我们首先要得到这个<SPAN lang=EN-US>IPrincipal</SPAN>对象，我这里写了一个方法实现。</SPAN></P>
<P align=left><SPAN lang=EN-US>private</SPAN><SPAN lang=EN-US> IPrincipal GetPrincipal()</SPAN></P>
<P align=left><SPAN lang=EN-US>{</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </SPAN><SPAN>获取票据（这里仅仅是要得到<SPAN lang=EN-US>principal</SPAN>的<SPAN lang=EN-US>key</SPAN>）</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt((<SPAN>string</SPAN>)Request.Cookies[FormsAuthentication.FormsCookieName].Value);</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>GuidToken token = <SPAN>new</SPAN> GuidToken(<SPAN>new</SPAN> System.Guid(ticket.UserData));</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </SPAN><SPAN>从缓存中读取</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>ISecurityCacheProvider securityCache = SecurityCacheFactory.GetSecurityCacheProvider();</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>IPrincipal principal = securityCache.GetPrincipal(token);</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>if</SPAN> ( principal == <SPAN>null</SPAN> ) </SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>{</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>Response.Redirect("~/Login.aspx");</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>} </SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN><SPAN>return</SPAN> principal;</SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P>
<P><SPAN>一般我们会将此方法放到基类中，以避免重复写。也可以实现为基页的一个属性。注意这里并没有将<SPAN lang=EN-US>Context.User</SPAN>或<SPAN lang=EN-US>HttpContext.Current.User</SPAN>作为<SPAN lang=EN-US>IPrincipal(</SPAN>实际上是<SPAN lang=EN-US>GenericPrincipal</SPAN>对象<SPAN lang=EN-US>)</SPAN>的驻留对象，所以你不能利用<SPAN lang=EN-US>Page.User</SPAN>或<SPAN lang=EN-US>(this.)User</SPAN>访问到此对象，但无论如何，不管是<SPAN lang=EN-US>WinForm</SPAN>程序还是<st1:RTX w:st="on"><SPAN lang=EN-US>Web</SPAN></st1:RTX>应用程序，你都要得到一个<SPAN lang=EN-US>IPrincipal</SPAN>对象才行，这里举的例子是基于<st1:RTX w:st="on"><SPAN lang=EN-US>Web</SPAN></st1:RTX>应用程序的，从原来加到<SPAN lang=EN-US>Cookies</SPAN>中的信息中还原了一个</SPAN><SPAN lang=EN-US>FormsAuthenticationTicket</SPAN><SPAN>对象，从而得到一个<SPAN lang=EN-US>IPrincipal</SPAN>对象，如果实在<SPAN lang=EN-US>WinForm</SPAN>的程序中就更简单了，可以以<SPAN lang=EN-US>Thread.CurrentPrincipal</SPAN>为驻留对象，当然你也可以自已建立并维护一个<SPAN lang=EN-US>IPrincipal</SPAN>对象。具体在写每一项需要授权的操作时，我们可以这样写代码。</SPAN></P>
<P align=left><SPAN lang=EN-US>bool</SPAN><SPAN lang=EN-US> authorized = <SPAN>false</SPAN>;</SPAN></P>
<P align=left><SPAN lang=EN-US>string</SPAN><SPAN lang=EN-US> rule = "Delete Issue";</SPAN></P>
<P align=left><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P align=left><SPAN lang=EN-US>IAuthorizationProvider ruleProvider = AuthorizationFactory.GetAuthorizationProvider();</SPAN></P>
<P align=left><SPAN lang=EN-US>authorized = ruleProvider.Authorize(GetPrincipal(), rule);</SPAN></P>
<P align=left><SPAN lang=EN-US>&nbsp;</SPAN></P>
<P align=left><SPAN lang=EN-US>if</SPAN><SPAN lang=EN-US>(authorized)</SPAN></P>
<P align=left><SPAN lang=EN-US>{</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </SPAN><SPAN>允许执行操作</SPAN></P>
<P align=left><SPAN lang=EN-US>}</SPAN></P>
<P align=left><SPAN lang=EN-US>else</SPAN></P>
<P align=left><SPAN lang=EN-US>{</SPAN></P>
<P align=left><SPAN lang=EN-US><SPAN>&nbsp;&nbsp;&nbsp;&nbsp; </SPAN>// </SPAN><SPAN>无权执行此项操作</SPAN></P>
<P><SPAN lang=EN-US>}</SPAN></P>
<P><SPAN>这里写的这些代码千万不要<SPAN lang=EN-US>Copy</SPAN>、<SPAN lang=EN-US>Paste</SPAN>到你的程序中，而是要整合到你的程序的架构中，这里只是非常简单的介绍了如何使用之，可能非常不完善</SPAN></P>
<P><SPAN>⑺</SPAN><SPAN>个性化设置</SPAN></P>
<P><SPAN>安全应用程序块还包含个性化设置功能，如果恰当应用此功能可以使您的程序更具人性化、界面更友好，使用时仿照配置应用程序块就可以了，我们就不在此详细介绍了。</SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN></SPAN><BR></P></SPAN><img src ="http://www.cnblogs.com/lifework/aggbug/207705.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41791/" target="_blank">[新闻]谷歌平台手机获准上市</a>]]></description></item><item><title>Enterprise LibraryV1.0-配置应用程序块</title><link>http://www.cnblogs.com/lifework/archive/2005/08/03/207039.html</link><dc:creator>生活、工作</dc:creator><author>生活、工作</author><pubDate>Wed, 03 Aug 2005 15:35:00 GMT</pubDate><guid>http://www.cnblogs.com/lifework/archive/2005/08/03/207039.html</guid><wfw:comment>http://www.cnblogs.com/lifework/comments/207039.html</wfw:comment><comments>http://www.cnblogs.com/lifework/archive/2005/08/03/207039.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.cnblogs.com/lifework/comments/commentRss/207039.html</wfw:commentRss><trackback:ping>http://www.cnblogs.com/lifework/services/trackbacks/207039.html</trackback:ping><description><![CDATA[摘要: 配置应用程序块(不当之处请各位指出)⑴概述 假设我们的一个应用程序可能会使用同类型的几个的数据库(如使用两个SQL Server)，也可能使用几个不同类型的数据库(如同时使用SQL Server和Sybase)，还可能现在使用一种(个)数据库，将来很有可能要换成另一种(个)，我们一般的解决办法是利用factory pattern，操作数据库的类根据外部的一个配置文件来创建，也就是说我们会在代码中读&nbsp;&nbsp;<a href='http://www.cnblogs.com/lifework/archive/2005/08/03/207039.html'>阅读全文</a><img src ="http://www.cnblogs.com/lifework/aggbug/207039.html?type=1" width = "1" height = "1" /><br><br><a href="http://news.cnblogs.com/n/41790/" target="_blank">[新闻]微软自称传媒公司 强调搜索引擎三大趋势</a>]]></description></item></channel></rss>