[FCKEditor]使用FCKeditor_2.6.3版本有关上传图片问题解决

mikel阅读(929)

使FCKeditor_2.6.3版本有关上传图片问题解决

问题:提示This connector is disabled

解决:
在fckeditor\editor\filemanager\connectors\aspx目录下面有一个 config.ascx 用户控件。打开它,我们可以看到有一个:private bool CheckAuthentication()的方法。
private bool CheckAuthentication()
 {
  // WARNING : DO NOT simply return "true". By doing so, you are allowing
  // "anyone" to upload and list the files in your server. You must implement
  // some kind of session validation here. Even something very simple as…
  //
  //  return ( Session[ "IsAuthorized" ] != null && (bool)Session[ "IsAuthorized" ] == true );
  //
  // … where Session[ "IsAuthorized" ] is set to "true" as soon as the
  // user logs in your system.
 
  return false;
  }
默认是返回false的,返回true就可以了。不要轻易的返回true,要不然任何人都可以上传文件到你的服务器。可以在这里验证session的值来判断用户是否可以上传文件。
 

问题:FCKEditor 图片上传一直显示进度条的问题


解决:

很多博主介绍配置FCKeditor的目录时都是这样写的,我不知道是不是大家互相复制的关系

<appSettings>
    <add key="FCKeditor:BasePath" value="~/FCKeditor/"/>
    <!–FCKeditor基本文件目录–>
    <add key="FCKeditor:UserFilesPath" value="/UserFiles/"/>
    <!–文件上传目录–>
  </appSettings>

大家看一下上面两个配置有什么区别没有,"~/FCKeditor/"比"/UserFiles/"前面多了符号“~”。

在“/UserFiles/”前面加上“~”符号就可以了。

这个错误还会导致下面的问题:在单击浏览服务器按钮是提示:未能映射路径“/Files/image/”,具体提示如下:

==========================================================================================

—————————
Microsoft Internet Explorer
—————————
The server didn't send back a proper XML response. Please contact your system administrator.

XML request error: Internal Server Error (500)

Requested URL:
http://localhost:7958/FCKeditorTest/FCKeditor/editor/filemanager/connectors/aspx/connector.aspx?Command=GetFoldersAndFiles&Type=Image&CurrentFolder=%2F&uuid=1224920141937

Response text:
<html>

    <head>

        <title>未能映射路径“/UserFiles/image/”。</title>

        <style>

         body {font-family:"Verdana";font-weight:normal;font-size: .7em;color:black;}

         p {font-family:"Verdana";font-weight:normal;color:black;margin-top: -5px}

         b {font-family:"Verdana";font-weight:bold;color:black;margin-top: -5px}

         H1 { font-family:"Verdana";font-weight:normal;font-size:18pt;color:red }

         H2 { font-family:"Verdana";font-weight:normal;font-size:14pt;color:maroon }

         pre {font-family:"Lucida Console";font-size: .9em}

         .marker {font-weight: bold; color: black;text-decoration: none;}

         .version {color: gray;}

         .error {margin-bottom: 10px;}

         .expandable { text-decoration:underline; font-weight:bold; color:navy; cursor:hand; }

        </style>

    </head>

 

    <body bgcolor="white">

 

            <span><H1>“/FCKeditorTest”应用程序中的服务器错误。<hr width=100% size=1 color=silver></H1>

 

            <h2> <i>未能映射路径“/UserFiles/image/”。</i> </h2></span>

 

            <font face="Arial, Helvetica, Geneva, SunSans-Regular, sans-serif ">

 

            <b> 说明: </b>执行当前 Web 请求期间,出现未处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

 

            <br><br>

 

            <b> 异常详细信息: </b>System.InvalidOperationException: 未能映射路径“/UserFiles/image/”。<br><br>

 

            <b>源错误:</b> <br><br>

 

            <table width=100% bgcolor="#ffffcc">

               <tr>

                  <td>

                      <code>

 

执行当前 Web 请求期间生成了未处理的异常。可以使用下面的异常堆栈跟踪信息确定有关异常原因和发生位置的信息。</code>

 

                  </td>

               </tr>

            </table>

 

            <br>

 

            <b>堆栈跟踪:</b> <br><br>

 

            <table width=100% bgcolor="#ffffcc">

               <tr>

                  <td>

                      <code><pre>

 

[InvalidOperationException: 未能映射路径“/UserFiles/image/”。]

   System.Web.Hosting.HostingEnvironment.MapPathActual(VirtualPath virtualPath, Boolean permitNull) +615

   System.Web.VirtualPath.MapPathInternal() +29

   System.Web.HttpRequest.MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, Boolean allowCrossAppMapping) +125

   System.Web.HttpServerUtility.MapPath(String path) +58

   FredCK.FCKeditorV2.FileBrowser.TypeConfig.GetFilesDirectory() +63

   FredCK.FCKeditorV2.FileBrowser.FileWorkerBase.ServerMapFolder(String resourceType, String folderPath, Boolean isQuickUpload) +37

   FredCK.FCKeditorV2.FileBrowser.Connector.GetFolders(XmlNode connectorNode, String resourceType, String currentFolder) +21

   FredCK.FCKeditorV2.FileBrowser.Connector.OnLoad(EventArgs e) +708

   System.Web.UI.Control.LoadRecursive() +49

   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3743

</pre></code>

 

                  </td>

               </tr>

            </table>

 

            <br>

 

            <hr width=100% size=1 color=silver>

 

            <b>版本信息:</b>&nbsp;Microsoft .NET Framework 版本:2.0.50727.42; ASP.NET 版本:2.0.50727.42

 

            </font>

 

    </body>

</html>

<!–

[InvalidOperationException]: 未能映射路径“/UserFiles/image/”。

   在 System.Web.Hosting.HostingEnvironment.MapPathActual(VirtualPath virtualPath, Boolean permitNull)

   在 System.Web.VirtualPath.MapPathInternal()

   在 System.Web.HttpRequest.MapPath(VirtualPath virtualPath, VirtualPath baseVirtualDir, Boolean allowCrossAppMapping)

   在 System.Web.HttpServerUtility.MapPath(String path)

   在 FredCK.FCKeditorV2.FileBrowser.TypeConfig.GetFilesDirectory()

   在 FredCK.FCKeditorV2.FileBrowser.FileWorkerBase.ServerMapFolder(String resourceType, String folderPath, Boolean isQuickUpload)

   在 FredCK.FCKeditorV2.FileBrowser.Connector.GetFolders(XmlNode connectorNode, String resourceType, String currentFolder)

   在 FredCK.FCKeditorV2.FileBrowser.Connector.OnLoad(EventArgs e)

   在 System.Web.UI.Control.LoadRecursive()

   在 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

[HttpUnhandledException]: 引发类型为“System.Web.HttpUnhandledException”的异常。

   在 System.Web.UI.Page.HandleError(Exception e)

   在 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

   在 System.Web.UI.Page.ProcessRequest(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

   在 System.Web.UI.Page.ProcessRequest()

   在 System.Web.UI.Page.ProcessRequestWithNoAssert(HttpContext context)

   在 System.Web.UI.Page.ProcessRequest(HttpContext context)

   在 ASP.fckeditor_editor_filemanager_connectors_aspx_connector_aspx.ProcessRequest(HttpContext context)

   在 System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()

   在 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

–>
—————————
确定  
—————————

============================================================================================================

以上问题都是根据测试推断出的,我并不知道源码是怎么处理的,希望高手能给予指点。

相关链接:

FCKeditor_2.6.3的安装使用方法

[C#]WebService序列化

mikel阅读(1213)

 1 /// <summary>
 2     /// 序列化DataTable
 3     /// </summary>
 4     /// <param name="pDt">包含数据的DataTable</param>
 5     /// <returns>序列化的DataTable</returns>
 6     private static string SerializeDataTableXml(DataTable pDt) {
 7         // 序列化DataTable
 8         StringBuilder sb = new StringBuilder();
 9         XmlWriter writer = XmlWriter.Create(sb);
10         XmlSerializer serializer = new XmlSerializer(typeof(DataTable));
11         serializer.Serialize(writer, pDt);
12         writer.Close();
13 
14         return sb.ToString();
15     }
16 

 1 /// <summary>
 2         /// 反序列化DataTable
 3         /// </summary>
 4         /// <param name="pXml">序列化的DataTable</param>
 5         /// <returns>DataTable</returns>
 6         public static DataTable DeserializeDataTable(string pXml) {
 7 
 8             StringReader strReader = new StringReader(pXml);
 9             XmlReader xmlReader = XmlReader.Create(strReader);
10             XmlSerializer serializer = new XmlSerializer(typeof(DataTable));
11 
12             DataTable dt = serializer.Deserialize(xmlReader) as DataTable;
13 
14             return dt;
15         }
16

[Flex]用FlexMonkey测试Flex

mikel阅读(985)

在这篇新闻中,InfoQ.com与FlexMonkey项目创建者、来自Gorilla Logic的Stu Stern一起讨论了FlexMonkey。FlexMonkey是一个用来测试Flex应用的开源工具,它采用记录/回放模式。

Stern首先对FlexMonkey进行了概要性描述:

FlexMonkey是一个开源的Flex应用和库,可以记录和回放用户界面的交互并生成可重复使用的测试用例,你可以在持续集成框架(如Cruise Control)中运行这些测试用例。

接下来InfoQ问询了FlexMonkey与其他Flex测试工具有何区别:

我们注意到FlexMonkey是唯一一个完全基于Flex的记录/回放工具。它无需浏览器插件(当然除了Flash player以外),也用不着特殊的脚本语言。记录下来的UI场景可以作为ActionScript的源代码,它们既可读又可编辑。甚至不需要使用记录工 具就可以轻松创建测试,因为利用FlexMonkey API,我们没必要直接在ActionScript中指定大多数的UI操作。

如果你了解Flex,那么你对FlexMonkey的使用就几乎没有任何问题,能运行Flex的地方就能运行FlexMonkey(也就是说几乎任何平台都可以)。

接下来,Stern谈到了许可协议:

FlexMonkey本身基于Apache 2许可,这意味着你可以修改其源代码,并将其作为其它开源或商业产品的一部分。很显然,Gorilla Logic认为将FlexMonkey放出价值更大,而并不是将其作为一个产品来卖!

但我们应该注意到FlexMonkey依赖于Adobe的Flex Automation API,它位于FlexBuilder专业版中。FlexBuilder标准版包含了Automation API的试用版,这限制了应用每次加载时所允许的重放次数。你可以通过FlexBuilder标准版来试用FlexMonkey,如果觉得满意再升级到专 业版。我们希望Adobe将Automation API加到大多数已开源的Flex平台中,但现在你不得不使用FlexBuilder专业版。专业版还可以进行性能与内存分析,因此如果你正在进行要求严 格的企业级开发,那么无论如何也要升级到专业版。

InfoQ谈到了测试异步功能所面临的挑战:

FlexMonkey所生成的每个UI事件都是异步的,而且FlexMonkey都会在生成每个后续事件前暂停(默认暂停500毫秒)。如果你有一个运行时间很长的数据库查询(比如由按钮点击所触发),那么在按钮事件产生后你可以增加延迟时间。

像FlexUnit这样的测试运行框架也需要针对异步操作进行特殊的处理。FlexMonkey API本身就是异步的,因此你需要通过某种手段告诉运行器测试何时完成。当FlexMonkey在一个测试用例中处理完所有的事件时它会产生一个 READY_FOR_VALIDATION事件。如果使用FlexUnit,你需要在调用下一个测试用例前使用FlexUnit的addAsync方法来 告诉FlexUnit等待这个事件完成。

我还要补充一下,尽管FlexUnit是当前唯一一个直接集成到FlexMonkey中的 xUnit框架,但你仍然可以将其他针对Flex的xUnit框架与FlexMonkey搭配使用。我再强调一次,FlexMonkey测试其实就是 ActionScript。此外,你可以直接将其他任何xUnit框架集成到FlexMonkey中并生成代码,就像我们对FlexUnit所做的一样 (请放心大胆地去做吧)。

InfoQ紧接着问道,FlexMonkey是如何使用Flex Automation框架的:

当你运行一个使用了Automation API的应用时,每个UI事件(如按钮或者鼠标点击)都会生成一个“automation事件”来描述相应的UI事件。这种包含在automation事 件中的信息可以反馈到Automation API中并产生一个假的按钮点击,而Flex对其的响应就好像它是原生的UI事件一样。正如我之前所提到的,通过使用FlexMonkey API,你可以创建一个此前并没有被Automation API记录下来的事件。我们可以使用FlexMonkey记录并回放几乎任何Flex UI事件,包括相关的“exotic”操作,比如某个DataGrid里的itemEditor是一个ComboBox,那么我们就可以从中选择一个条 目。

最后,InfoQ问Stern,读者还需了解什么:

尽管我们的开源项目还是个新兵,但FlexMonkey却相当稳定。它有丰富的文档。大多数人能在不到一小时内就成功运行起来。

你可以运行http://keystone.gorillalogic.com/~sstern/MonkeyContacts.html上的这个示例应用来快速了解FlexMonkey。如果你运行这个示例,你会看到记录并回放UI场景是多么的简单,你还能看到生成的ActionScript是什么样子的。

该项目本身位于http://flexmonkey.googlecode.com。我们希望看过这篇新闻的每个人都能加入我们这个快速发展的社区!

查看英文原文:Flex Testing with FlexMonkey

[C#]C#反射程序集

mikel阅读(1117)

近来刚好在看一些关于反射的东西,之前一直没用过,真是惭愧,后来看了些资料后,觉得好像也不怎么难,就一句话:
   Assembly.Load(Assembly.GetExecutingAssembly().FullName).CreateInstance(Namespace + "." + ClassName);就搞掂了,

       但是很多貌似简单(或许真的是很简单吧)的问题就搞了我很久,就是关于那个程序集的问题,一开始的时候,

       只是知道Assembly.Load("程序集名")这个,只是知道用Assembly.GetExecutingAssembly().FullName这可以拿到了,

      看似一切都是那么的简单,但是当我在调试时,却发现我反射不出那个类,找了些资料才知道,原来是程序集出了问题了.

      正确点来说,是我用错了程序集,对于很多新人来说,或是刚接触的反射的来说,可能并不怎么了解那些程序集是怎么一回事.

      我做了测试后得出:
       1.类
              所有的类是在同一个程序集中,所有的类都在App_Code的文件夹下(默认),在App_Code里,不管你分几个文件夹存放那些类,不管
              类中有多少个namespace,都只有一个程序集.
       2.用户控件
             每一个UserControl属于不同的程序集,不管你是否放在同一个文件夹里.
       3.页面
             每一个不同的页面都属于同一个程序集.不管程序代码的命名空间是否一致.在运行时,所有页面的命令空间会默认显示
             为"ASP",而你加的命名空间,则和页面的名字加起来统称为基类名了.

       关于反射Assembly.Load("程序集").CreateInstance("命名空间.类"),而不管在哪一层写这段代码其中的("程序集")
读取的实际是web层bin文件夹下的dll,也就是说你反射的类的程序集dll在web层的bin下必须有,当我们的程序运行或发布时,.Net工具会自动
的添加当前项目的类的dll,页面的dll,还有用户控件的dll.
       注意CreateInstance()一定是命名空间.类名,否则创建的实例为空
              Assembly.Load("程序集名")
              Assembly.LoadFrom("程序集实际路径")
  希望我这些总结可以给那些刚入门的朋友一点帮助,本人才疏学浅,要是有什么不对的地方,还忘朋友倍们指出.

在此赋上我的测试代码.Reflect程序集测试例子  ,大家下载后,用VS中打开后,可以直接浏览default,和default2这两个页面,并做些对比,相信这时你会明白很多.

[C#]应用系统架构设计

mikel阅读(876)

应用系统架构设计

应用系统架构设计  Simonw@2005.4.24 如有转载请注明出处。
 

我 们在做着表面上看似是对于各种不同应用的开发,其实背后所对应的架构设计都是相对稳定的。在一个好的架构下编程,不仅对于开发人员是一件赏心悦目的事情, 更重要的是软件能够表现出一个健康的姿态;而架构设计的不合理,不仅让开发人员受苦受难,软件本身的生命周期更是受到严重威胁。这里我将针对在微软 dotNet平台上做应用开发系统的一般架构流程设计做一个粗浅的讨论。

 

总体设计图 

 

表示层

表示层由UI(User Interface)和UI控制逻辑组成。

l         UI(User Interface)

UI是客户端的用户界面,负责从用户方接收命令,请求,数据,传递给业务层处理,然后将结果呈现出来。根据客户端的不同我们大体将应用程序分为BS(Browser-Server) 浏览器结构,CS(Client-Server)桌面客户端结构。

BS 的优点是无需操心客户端,只需要部署维护好服务器即可。CS的优点在于强大的界面交互表达能力。RIA(Rich Internet Application)是为了融合这两种结构优点的一种技术,它依赖在客户端一次性安装一个通用解释器之后即获得强大的界面交互表达能力和无需部署具体 客户端的方便性。具体的实现技术很多,例如微软的SmartClient, Avalon; Macromedia的Flex;以JS为基础的Bindows;Ajax等等很多。

 

l         UI控制逻辑

UI 控制逻辑负责处理UI和业务层之间的数据交互,UI之间状态流程的控制,同时负责简单的数据验证和格式化等功能。具体的说在dotNet事件驱动的编程模 型下,UI控制逻辑被自然的实现在了事件函数中,例如PageLoad事件函数,ButtonClick事件函数。在这些事件函数中,主要任务就是做UI 控件与业务实体的数据交换与业务调用,但面对大量的数据交换工作量与维护量就成了最大的问题。而在复杂应用的系统中,状态与流程的管理是必须要考虑的因 素,它们同样是业务逻辑的一部分,如果不加以封装的直接写在事件函数中将导致业务依赖表示层。下面分别讨论这两个问题。

 

1.         1.UI与业务实体之间的数据交互

此 阶段负责数据交换的业务实体称为DTO(Data Transfer Object),处理输入时我们从UI控件的获得数据填入DTO再向下传播,处理输出时用户发出请求业务层会将数据以DTO的形式返出再赋给UI控件展 现。因此需要一种方式来自动解决这样的来回赋值问题。遗憾的是dotNet下的不少控件虽然支持数据绑定但仍然没有一个现成完整的解决办法。我们可以自己 设计一个Adapter按照某种映射关系来自动处理这样的绑定,这样的映射关系最好是UI控件与DTO属性的事先命名约定,以此种方式的约定作为映射关系 无需增加任何配置文件和配置工作即可实现。

 

2.         2.状态与流程的管理

既然是业务逻辑的一部分就不应该耦合再表示层当中。MVC(Model-View-Controller) 模式提供了实现这一目标的方法。Controller是整个方案的核心,它是一个流程管理器,来自UI所有的命令与数据经过Controller分发给业 务层或其他UI,这样我们可以把流程,权限等逻辑单独封装,例如配置文件中,达到最大化的业务重用。dotNet下MVC的方案并不像Java下有那么多 选择,目前有以下几种选择:

微软的UIPAB,它可以处理bs,cs下的流程跳转,可以使得相同的业务系统有webform和winform不同的展现方式。

开源的Mavrick.Net,它只适用于ASP.NET应用程序,它对流程,国际化,页面包装,xslt页面转换提供了很好的支持。

开源的Lattis,同样只适用于ASP.NET应用程序。

 

业务层

业务层封装了实际业务逻辑,包含数据验证,事物处理,权限处理等业务相关操作,是整个应用系统的核心。因此设计一个能够真实反映实际需要的业务层是非常必要的,我们将实际业务具体分为业务数据与业务操作两部分。

 

l         业务数据

业务数据又是业务逻辑的核心,最终业务数据将以一种固定的格式表现于内存中,在系统的各个层次间传输,充当DTO角色。表达业务数据的方式一般分为两种Table Model和Domain Model。

Table Model是将数据库中的表直接映射成为业务数据对象,这样的优点是适合于机器操作,ADO.NET直接提供了这种操作的便利,但对于复杂业务关系的表达 就很不直观。只适合于业务需求与数据表对应关系很直接的需要快速开发的情况。通常我们选用Dataset或者强类型Dataset(Strong Typed Dataset),强类型Dataset支持编译时的类型检查,效率上要略高于普通Dataset。Dataset有很多方便的特性:无需自己编写维护 类,支持序列化,数据副本保存,支持数据集合,对控件绑定支持效果好,微软提供了相应的生成工具以及持久方案。但缺点也是明显,复杂数据表现不直观,做为 DTO在各个层次间传输,尤其是分布式环境,庞大的体积,相对缓慢的实例化对于性能造成很大压力。

Domain Model则是根据实际业务按照现实方式用OO思想建模,这样很适合业务复杂的系统。通常采用自定义数据实体(Custom Data Entity)方式表达。自定义数据实体,有着良好的性能,编译时的类型检查,数据表现方式非常直观符合实际业务的操作方式等优点,但需要自己定义维护 类,在分布式环境下需要自己编写序列化方法。

综合各种因素考虑,虽然业务简单对应直接的系统我们以Table Model建模开发效率很高但难免保证系统日后不会变的复杂,因此出于复用性,扩展性,性能等方面选用Domain Model建模为佳。

 

l         业务操作

业务操作负责对业务数据进行各种业务相关的处理,例如验证,流向,整合,事物,权限等,但它不负责有关对数据源的操作。它与业务数据的关系设计有2种方式。

分 离业务数据与业务操作,将业务数据单独封装到只有数据get,set的数据类中,这个数据类只充当DTO。将业务操作封装到独立的service类中与业 务数据一起充当业务层。这样当系统不复杂的时候显的简单直观,而随着系统日益复杂,service类会变的杂乱,而将本身耦合紧密的数据与操作分离对于复 用也是不利的因素。具体可参考Martin Fowler 的贫血的Domain Model一文,但我并不倾向于业务层直接访问数据源。

整 合业务数据与业务操作,将业务数据与相关的业务操作封装在一起称为业务实体,业务实体作为统一的业务层为表示层提供服务,同时也负责作为DTO在各个层次 间传输,我倾向于这样完整的Domain Model设计方式,每个业务实体都可以做为一个单独组件形式存在,对于组件化复用有着莫大的好处。

 

l         业务模块间的依赖

各 个业务模块之间的依赖,有时候会是难以解决的问题,尤其是一些可以重复利用的业务组件,例如权限管理,邮件发送等等。管理好这些各种不同的业务组件是我们 的目标,IoC容器为我们提供了最完美的方案,通过它将不同的模块注入到系统中我们可以在不知道这个组件存在的情况下调用它。但目前只有不成熟的Spring.Net一个选择,我们只有一声叹息,因此也就不多讨论了。

 

业务数据访问层

业 务数据访问层是一个针对具体应用系统的专属层,它为业务层提供与数据源交互的最小操作方式,仅仅是业务层需要的数据访问接口,业务层完全依赖业务数据访问 层所提供的服务。这些服务负责从业务层接收数据或返回业务实体,它屏蔽了实际业务数据与机器存储方式的差别。当然,数据层选用抽象的解决方案同样可以达到 这个效果,但业务数据访问层最大的特点就是针对具体业务做抽象,而抽象的数据层访问方案是针对通用做抽象。往往业务中针对具体的设计生命力会变的更强,这 样我们可以最大限度的保持了上层代码的复用性,当需要更换存储策略如果数据层访问差别太大,通过更换数据层无法解决问题的时候我们最多只需要更换业务数据 访问层,而无需改变业务层。

 

业务数据访问层由DAO(Data Access Object)层和系统服务层两部分组成。DAO层为每个业务实体提供最基本的数据访问服务,系统服务层为系统全局提供与业务关系不大的通用数据访问服务,这两层处于系统中的同一个层次位置。

 

业务层与业务数据访问层关系图

 

 

数据层

数据层的宗旨就是为数据源提供一个可供外界访问的接口,我们应该选用一种能够提供数据源无关的抽象数据访问接口并通过在其下挂接各种不同的DataProviador来访问数据源的数据层组件,这样做便于移植到不同的数据源上。目前有以下3种数据层方案:

 

1.        1. 封装ADO.Net

这 些数据访问组件都是基于ADO.Net的浅封装,它的优点在于封装层次低所以速度最快,我们可以手动组织SQL语句用来适应复杂的操作以及个性的优化等。 缺点是无法直接处理自定义数据实体方式的业务实体对象,需要将业务实体中的数据属性以参数形式传入传出。这样的方式虽然最为保险,但随着系统规模增大,开 发效率,质量,,后期的维护,二次开发都变成尤为突出的问题,对开发人员的要求会变的越来越高。另外对于事物操作封装不是很好,无法提供声明性事物,经常 会在业务层出现访问数据层的需要。这样的组件目前应用的很广泛,例如微软在EnterpriseLibrary中提供的DAAB(Data Access Application Block),还有以前的DAAB3.1。EnterpriseLibrary是个成熟的产品,包括了数据访问,异常,日志,缓存,加密,配置,安全等组件做为通用服务非常适合。

 

2.        2. or-Mapping组件

ORM 是最好的数据持久解决方案,它的优点在于能够以面向对象的方式操纵数据,因此可以直接处理自定义数据实体的业务对象,我们根本不用操心SQL语句以及底层 存储方式,这样极大的简化的代码提高了开发效率,对于日后维护扩展都带来极大的便利。缺点在于屏蔽了底层使得我们无法针对具体数据源做优化,而且对于复杂 关联的sql操作有些力不从心,同时性能也差一些但辅助以缓存情况会好很多,而在dotNet下最大的问题就是没有一个成熟便宜的ORM产品供我们使用, 全部都是beta版本和商业版本。这些版本或多或少都存在一些问题,以至于真正应用中需要经过仔细考察。例如 NHibernate,Gentle.Net,XPO,Grove.Net等等非常多。

 

3.        3. DataMapper(SqlMapper)

SqlMapper为以上两种方式提供了一个折中的选择,它可以以面向对象的方式直接处理自定义数据实体的业务对象,同时可以根据与数据源与业务实体的映射关系执行手写的sql语句,这样完全使得我们可以针对具体数据源做优化,对于复杂操作同样可以胜任。目前只有iBatis.Net一个产品,它是一个java移至的开源项目,已经比较成熟,可以在无需编译的情况下随意替换DAO。

 

 

至 此,整个架构方案的讨论已经完成,我们可以看出dotNet下可供选择的解决方案是那么的有限,反看Java世界,有那么多成熟可供利用的组件框架,流口 水中…不过dotNet也正在走向成熟,我们需要时间等待。这个架构设计的思路只代表了我个人的理解,而且也并不是说所有的开发都是这么一套方案,在 具体环境中需要做具体的调整。希望能起到一个抛砖引玉的作用。我的邮箱是i-simon AT msn.com,由于我经验尚浅,有不正确或不足的地方欢迎指正讨论,另外本文将根据技术的最新进展持续更新。
应用系统架构设计-补全版:http://simonw.cnblogs.com/archive/2005/10/27/263145.html

[SQL]临时表和表变量

mikel阅读(1186)

我们经常使用临时表和表变量,那现在我们就对临时表和表变量进行一下讨论.

临时表
局部临时表
全局临时表
表变量

                              
临时表
临 时表存储在TempDB数据库中,所有的使用此SQL Server 实例的用户都共享这个TempDB,因为我们应该确保用来存储TempDB数据库的 硬盘有足够的空间,以使之能够自己的增长.最好能够存储在一个拥有独立硬盘控制器上.因为这样不存在和其它的硬盘I/O进行争用.
 
我 们很多程序员认为临时表非常危险,因为临时表有可能被多个连接所共享.其实在SQL Server中存在两种临时表:局部临时表和全局临时表,局部临时表 (Local temp table)以#前缀来标识,并且只能被创建它的连接所使用.全局临时表(Global temp table)以##前缀来进 行标识,并且可以和其它连接所共享.
 
局部临时表
局部临时表不能够被其它连接所共享的原因其实是在SQL Server 2000中自动为局部临时表的表名后面加上了一个唯一字符来标识.如: 
     Create TABLE [#DimCustomer_test]
     (
        [CustomerKey] [int]
        ,   [FirstName] [nvarchar](50)  
    ,[MiddleName] [nvarchar](50)  
    ,[LastName] [nvarchar](50) 
        )

现在我们来查看一下TempDB中 sysobjects表,我们会发现我们新创建的临时表#DimCustomer_test已经被加上了后缀:
 
  USE TempDB
  GO
  Select name FROM sysobjects Where name LIKE ’%DimCustomer%’

 
the Result is:
name
#DimCustomer_test___________________________________________________________________________________________________000000000005
全局临时表
下面我们来看一下全局临时表:
     Create TABLE [##DimCustomer_test]
     (
        [CustomerKey] [int]
        ,      [FirstName] [nvarchar](50)  
    ,[MiddleName] [nvarchar](50)  
    ,[LastName] [nvarchar](50) 
        )

现在我们来查看一下TempDB中 sysobjects表,我们会发现我们新创建的临时表##DimCustomer_test没有被加上了后缀:
 
  USE TempDB
  GO
  Select name FROM sysobjects Where name LIKE ’%DimCustomer%’

 
The Result are:
#DimCustomer_test___________________________________________________________________________________________________000000000005
##DimCustomer_test
 
–Drop test temp tables
                              Drop TABLE [##DimCustomer_test]
                              Drop TABLE [#DimCustomer_test]

 
可以看到我们刚才创建的全局临时表名字并没有被加上标识.
 
表变量
表变量和临时表针对我们使用人员来说并没有什么不同,但是在存储方面来说,他们是不同的,表变量存储在内存中.所以在性能上和临时表相比会更好些!
 
另一个不同的地方是在表连接中使用表变量时,要为此表变量指定别名.如:
 
  USE AdventureWorksDW
  GO
  DECLARE @DimCustomer_test TABLE 
  (
     [CustomerKey] [int]
     ,      [FirstName] [nvarchar](50)  
 ,[MiddleName] [nvarchar](50)  
 ,[LastName] [nvarchar](50) 
     )
  —insert data to @DimCustomer_test
  Insert @DimCustomer_test 
  (
     [CustomerKey]  
     ,      [FirstName]  
 ,[MiddleName]  
 ,[LastName] 
     )
  Select  
     [CustomerKey]  
     ,      [FirstName]  
 ,[MiddleName]  
 ,[LastName] 
  FROM DimCustomer
  Select [@DimCustomer_test].CustomerKey,SUM(FactInternetSales.OrderQuantity)
 FROM @DimCustomer_test  INNER JOIN FactInternetSales   ON
 @DimCustomer_test.CustomerKey = FactInternetSales.CustomerKey
 Group BY CustomerKey

 
Result:
 
Server: Msg 137, Level 15, State 2, Line 32
Must declare the variable ’@DimCustomer_test’.
 
 
如果我们对上面的查询进行更改,对查询使用别名(并且找开IO):
—–in the follow script,we used the table alias.
 DECLARE @DimCustomer_test TABLE 
 (
    [CustomerKey] [int]
    ,      [FirstName] [nvarchar](50)  
,[MiddleName] [nvarchar](50)  
,[LastName] [nvarchar](50) 
    )
 Insert @DimCustomer_test 
 (
    [CustomerKey]  
    ,      [FirstName]  
,[MiddleName]  
,[LastName] 
    )
 Select  
    [CustomerKey]  
    ,      [FirstName]  
,[MiddleName]  
,[LastName] 
 FROM DimCustomer
 Select t.CustomerKey,f.OrderQuantity
 FROM @DimCustomer_test t INNER JOIN FactInternetSales  f ON
 t.CustomerKey = f.CustomerKey
 where t.CustomerKey=13513

 
表变量在批处理结束时自动被系统删除,所以你不必要像使用临时表表一样显示的对它进行删除.

[MVC]Asp.Net Mvc Beta新特性之自动绑定(2)-深入探索篇

mikel阅读(787)

在上篇中介绍了自动绑定的基本用法,在本篇中,我们将深入了解自动绑定的工作原理.

自动绑定的确是让人感到兴奋的特性,然而,为了让它可以在我们的项目中更好的工作,我们有必要深入了解如何更进一步细调该特性以及它是如何工作的.而本文正式即将揭开这个谜底.

为了更好的了解该特性,我们有必要到codeplex去下载一份ASP.NET mvc的源代码并分析之,在本文写作的时候,codeplex上已经放上了beta版的源码,如果想进一步了解的朋友可以下载并对照本文分析.

在beta版中,新增了自动绑定这一特性,并对绑定特性做了一定的修改

1.     新增BindAttribute:自动绑定特性设置

2.     修改DefaultModelBinder:自动绑定的实现部分

3.     修改ControllerActionInvoker:绑定的调用入口

4.     新增ModelBinderContext,封装绑定所需数据

5.     新增BinderResult,封装绑定结果

其他不大重要的修改略过

    我们一步步来分析绑定的执行过程,首先肯定在ControllerActionInvoker中,看到GetParameterValue方法:

这 儿便是对每个参数都尝试调用ModelBinder来绑定参数,这儿的GetModelBinder方法和P5的一样,在我们自定义 ModelBinder的情况下可以进行自定义绑定,然而在自动绑定的时候获取的则是DefaultModelBinder,然后在 GetPropertyFilter方法中通过查阅BindAttribute来获取关于绑定的设置.最后对数据进行绑定.

    关 键的,我们需要对DefaultModelBinder进行分析,然而在此之前,还有一个类也是需要我们仔细看看的,那就是BindAttribute, 该特性是用来修饰参数的,它有4个重要的属性:Include,Exclude,Prefix和一个方法:IsPropertyAllowed,分别用来 设定:绑定的字段,不绑定的字段,参数前缀和判断给定的字段是否设定运行绑定,且该方法会作为一个Predicate委托封入 ModelBinderContext传入BindModel方法.

    现在来讨论使用默认绑定的情况,首先给出DefaultModelBinder的所有方法:

分别简介下这些方法的作用:

1.     BindModel:对外的调用接口,根据传入的ModelBinderContext绑定值

2.     BindModelCore:绑定自定义类型,自定义类型数组或者自定义类型字典

3.     BindProperty:绑定某个指定的属性,(此处是一个递归调用,仍然调用DefaultModelBinder进行属性的绑定,也就是说,理论上DefaultModelBinder可以对任意深度的属性进行绑定)

4.     ConvertSimpleArrayType,ConvertSimpleType,用来做类型转换,一个转换数组一个转换普通类型

5.     CreateArray:创建一个数组对象

6.     CreateModel:创建一个普通对象

7.     CreateSubIndexName:创建子索引名,命名方式为prefix[indexName]

8.     CreateSubPropertyName:创建子属性名,命名方式为prefix.propertyName

9.     GetBinder:获取modelType的Binder对象

10.   GetElementType:获取一个类型的ElementType

11.   GetSimeType:对给定的ModelBinderContext进行简单值绑定(也就是调用该方法时假定获取的数据是简单类型)

12.   IsCollectionInserface:判断是否是数组类型

13.   IsDictinaryInterface:判断是否是字典类型

14.   IsSimpleType:是否是简单类型(在这儿判断是否是值类型或者string)

15.   TryUpdateSimpleCollection:尝试绑定一个简单数组(即绑定Collection或者Collection,且T能通过IsSimpleType)

16.   UpdateCollection:绑定一个数组,该数组一般为Collection,且T是自定义类型

17.   UpdateDictionary:绑定一个字典类型

通 过以上方法的浏览,我们发现,DefaultModelBinder可以进行绑定的数据很多,包括简单类型(值类型和string).自定义类型,数组, 字典,同时由于采用了递归调用,理论上可以绑定任意深度的数据,由于这儿绑定的调用比较复杂,且本人表达能力有限,本文将不再详细讲述绑定的工作流程,下 面将总结下绑定的规则:

1.     默认绑定采用反射的方式将数据绑定到所需对象上

2.     绑定参数有可选特性BindAttribute定义

3.     BindAttribute中可以手动设置允许绑定的属性或者不允许绑定的属性,url取参前缀

4.     默认情况下所有属性都进行绑定

5.     DefaultModelBinder采用递归对所有需要绑定的参数进行绑定

6.     普通参数的命名方式为:prefix.protertyName,且默认情况下prefix为类型名.如对象MyData拥有类型为string的属性Name,那么在ValueProvider中取值的名称应该为mydata.name,命名不分大小写

7.     简单数组命名方式为prefix.protertyName,其中proertyName为数组名,可以存在多个

8.     对于自定义类型数组和字典,绑定必须提供如下参数:

1.     index-必须提供一个或多个,指示绑定的子元素名,可存在多个

2.     数 组必须提供一个或者多个以protertyName[indexName].subProtertyName形式结尾的表单数据,其中 protertyName为数组名,indexName为index中定义过的名称,subProteryName为自定义类型的属性名.

3.     字 典必须提供一个或者多个protertyName[indexName].key和protertyName[indexName].value的表单数 据,其中protertyName为字典名称,indexName为index中定义过的名称,key代表字典中的key绑定,value代表字典中的 value绑定,如果key或者value为非简单类型,表单的定义继续参照前面.

9.     对于以上绑定,如果子级属性为复杂类型,可以依次按照此规则命名表单数据

下面我们通过例子来分别描述上面的规则:

    在 体验篇中,我们看到对article的表单命名为article.title,article.content等,这就是根据第6条,父类和属性用.来间 隔,对简单数据的命名,article.tags,多个同名表单代表多个数族元素,参考第7条,在这儿,如果在article参数中显式定义了 prefix,则需要对应对表单名进行修改.对于属性为自定义类型的,继续使用.间隔,比如上文中的 article.advancearticle.today等,此处参考第7,9条,这些部分的例子可以参考第一篇,本篇中将不再说明.

    本篇中将重点介绍自定义类型数组,字典以及多级属性绑定,下面展示如何绑定一个ICollection,T为自定义类型,比如在AdvanceArticle中添加一个属性Reads,这是一个自定义类型,包含Name和Source,实例代码如下;

然后根据规则在表单aspx中加入:

这 儿给这个数组传入了两个元素,分别对应article.reads.index中的0和aa,下面的article.reads[0].name和 article.reads[0].source表示第一个元素的子属性,依次类推.如果您在应用中需要在数组中添加更多元素,可以采用js动态添加表单 域的方法,不过每次添加必须对应添加index和对应的子属性表单.

然后是绑定Dictinary,我们在AdvanceArticle中继续加入新属性:

然后在aspx中加入表单:

和 数组类似的,需要定义article.source.index,此处也显式定义了两个字典元素,分别是user和vip(注意,这儿的user和vip 并不代表字典key,字典key是需要从表单绑定的),然后分别定义article.source[user].key和 article.source[user].key等.如果有更多的元素,也需要按照此规则加入.

同样的,这儿的key和value也不局限于简单类型,如果key和value是自定义类型,则可以按照前面的规则继续向下定义.

最后需要说明几点: By Leven
2008-10-22

1.     绑定取值不限于表单域,在Beta中加入了ValueProvider,只要是能在ValueProvider中能取到的值都能绑定.

2.     自动绑定是基于反射,如果你嫌效率不够高,请采用自定义ModelBinder的方式对特定的类型进行特定的绑定已提升效率

3.     自 动绑是单向的,如果想实现在route中能接受该参数,可以采用重写绑定对象的ToString方法输出对应QueryString的方式,也就是说,如 果想在调用Url.Action(“xxx”,new { article= myArticle })能正确传入传出参数,可以重写Article的ToString方法,在提供一个类似article.title=xxx& article.content=xxx的字符串格式提供一个帮助类,将将数据分别放入ValueProvider,而调用Url方法时采用类似 Url.Action(“xxx”,article.ToRouteValue())的方法.

 

By Leven
2008-10-22

[Python]Python相关资源

mikel阅读(703)

Python相关资源

Python基本安装:

* http://www.python.org/ 官方标准Python开发包和支持环境,同时也是Python的官方网站
* http://www.activestate.com/ 集成多个有用插件的强大非官方版本,特别是针对Windows环境有不少改进;

Python文档:

* http://www.python.org/doc/current/lib/lib.html Python库参考手册。
* http://www.byteofpython.info/ 可以代替Tutorial使用,有中文译版的入门书籍。
* http://diveintopython.org/ 一本比较全面易懂的入门书,中文版翻译最近进步为很及时的5.4了。
* http://www.python.org/peps/pep-0008.html 建议采用的Python编码风格。
* http://doc.zoomquiet.org/ 包括Python内容的一个挺全面的文档集。

常用插件:

* http://www.pfdubois.com/numpy/ Python的数学运算库,有时候一些别的库也会调用里面的一些功能,比如数组什么的;
* http://www.pythonware.com/products/pil/ Python下著名的图像处理库Pil;
* http://simpy.sourceforge.net/ 利用Python进行仿真、模拟的解决方案;
* Matplotlib 据说是一个用来绘制二维图形的Python模块,它克隆了许多Matlab中的函数, 用以帮助Python用户轻松获得高质量(达到出版水平)的二维图形;
* http://www.amk.ca/python/code/crypto python的加解密扩展模块;
* http://cjkpython.i18n.org/ 提供与python有关的CJK语言支持功能:转码、显示之类。
* Psyco、Pyrex:两个用于提高Python代码运行效率的解决方案;
* Pyflakes、PyChecker、PyLint:都是用来做Python代码语法检查的工具。
* http://wxpython.sourceforge.net/ 基于wxWindows的易用且强大的图形界面开发包wxPython;
* http://www.pygame.org/ 用Python帮助开发游戏的库,也可以用这个来播放视频或者音频什么的,大概依靠的是SDL;
* http://starship.python.net/crew/theller/py2exe/ win下将Python程序编译为可执行程序的工具,是一个让程序脱离Python运行环境的办法,也可以生成Windows服务或者COM组件。其他能 完成Python脚本到可执行文件这个工作的还有Gordon McMillan's Installer、Linux专用的freeze以及py2app、setuptools等。不过此类工具难免与一些模块有一些兼容性的问题,需要现用 现测一下。
* 嵌入式数据库:BerkeleyDB的Python版,当然还有其他的好多。
* PEAK提供一些关于超轻量线程框架等基础性重要类库实现。

部分常用工具:

* http://www.scons.org/ Java有Ant这个巨火的构建工具,Python的特性允许我们构建更新类型的构建工具,就是scons了。
* Python Sidebar for Mozilla FireFox的一个插件,提供一个用来查看Python文档、函数库的侧边栏。
* IPython 很好用的Python Shell。wxPython发行版还自带了PyCrust、PyShell、PyAlaCarte和PyAlaMode等几个工具,分别是图形界面Shell和代码编辑器等,分别具有不同特点可以根据自己的需要选用。
* Easy Install 快速安装Python模块的易用性解决方案。

推荐资源:

* Parnassus山的拱顶 巨大的Python代码库,包罗万象。既可以从上面下载代码参考学习,同时也是与Python有关程序的大列表。
* Python号星际旅行船 著名Python社区,代码、文档、高人这里都有。
* faqts.com的Python程序设计知识数据库 Python程序设计知识库,都是与Python有关的程序设计问题及解决方法。
* 啄木鸟 Pythonic 开源社区 著名的(也可以说是最好的)国内Python开源社区。

代码示例:

* http://newedit.tigris.org/technical.htm Limodou的NewEdit编辑器的技术手册,讨论了一些关于插件接口实现、i18实现、wxPython使用有关的问题,值得参考。

其他东西:

* http://www.forum.nokia.com/main/0,,034-821,00.html Nokia居然发布了在Series 60系统上运行Python程序(图形界面用wxPython)的库,还有一个Wiki页是关于这个的:http: //www.postneo.com/postwiki/moin.cgi/PythonForSeries60 。Python4Symbian这个页面是记录的我的使用经验。
* pyre:使用Python完成高性能计算需求的包,真的可以做到么?还没研究。
* Parallel Python:纯Python的并行计算解决方案。相关中文参考页面
* Pexpect:用Python作为外壳控制其他命令行程序的工具(比如Linux下标准的ftp、telnet程序什么的),还没有测试可用程度如何。
* pyjamas:Google GWT的Python克隆,还处在早期版本阶段。
* Durus:Python的对象数据库。

有意思的东西:

* Howie:用Python实现的MSN对话机器人。
* Cankiri:用一个Python脚本实现的屏幕录像机。

参考资料

* ZDNET文章:学习Python语言必备的资源
* Pythonic Web 应用平台对比
* 在wxPython下进行图像处理的经验 (其实,仅使用wxPython也可以完成很多比较基础的图像处理工作,具体可以参照《wxPython in Action》一书的第12节)
* 通过win32扩展接口使用Python获得系统进程列表的方法
* 如何获得Python脚本所在的目录位置
* Python的缩进问题
* py2exe使用中遇到的问题
* idle的中文支持问题
* 序列化存储 Python 对象

Python IDE

我的IDE选择经验

* http://www.xored.com Trustudio 一个基于Eclipse的、同时支持Python和PHP的插件,曾经是我最喜欢的Python IDE环境,功能相当全了,不过有些细节不完善以致不大好用。
* http://pydev.sourceforge.net/ 另一个基于Eclipse的,非常棒的Python环境,改进速度非常快,现在是我最喜欢的IDE。
* http://www-900.ibm.com/developerWorks/cn/opensource/os-ecant/index.shtml 用 Eclipse 和 Ant 进行 Python 开发
* http://www.die-offenbachs.de/detlev/eric3.html ERIC3 基于QT实现的不错的PYTHON IDE,支持调试,支持自动补全,甚至也支持重构,曾经在Debian下用它,但图形界面开发主要辅助qt,我倾向wxpython,所以最后还是放弃了 这个。
* http://www.scintilla.org/ 同时支持Win和Linux的源代码编辑器,似乎支持Python文件的编辑。
* http://boa-constructor.sourceforge.net/ 著名的基于WxPython的GUI快速生成用的Python IDE,但是开发进度实在太差了……
* http://pype.sourceforge.net/ 成熟的Python代码编辑器,号称功能介于EMACS和IDLE之间的编辑器。
* http://www.stani.be/python/spe SPE:号称是一个Full Featured编辑器,集成WxGlade支持GUI设计。

[mvc]ASP.NET MVC Framework体验(5):路径选择(URL Routing)

mikel阅读(799)

概述

在传统的ASP.NET应用程序中,发生一个请求之后将映射到一个.aspx文件,且会对应到一个具体的物理aspx文 件。ASP.NET MVC Framework中,进入一个请求之后,不再是映射到.aspx文件,而是由MVC Framework中提供的路径选择引擎,映射到正确的Controller和Action执行请求。ASP.NET MVC Framework中URL路径映射规则定义在Global.asax中,这在一定程序上带来了很多的灵活性,如果想要修改URL应用结构的话,只需要修 改一下映射规则即可,并不需要修改Controller和View中的代码,但是这仍然不够灵活。改变Global.asax代码仍然会导致应用程序的重 新编译,本篇文章末尾部分将会介绍如何通过HttpModule定义映射规则在Web.config中。

理解路径选择

先看一下下面这幅图:

TerryLee_MVC_010 

从上图中可以看到,发生一个请求之后,第一步就是路径选择,而映射规则是通过在Routes集合中添加Route实例来注册的,如:

RouteTable.Routes.Add(
new Route
{
Url = "[controller]/[action]",
Defaults = new { action = "Index"},
RouteHandler = typeof(MvcRouteHandler)
});

Route中有四个属性,它的定义如下:

public class Route
{
public Route();
public Route(string url, Type routeHandler);
public Route(string url, object defaults, Type routeHandler);
public object Defaults { get; set; }
public Type RouteHandler { get; set; }
public string Url { get; set; }
public object Validation { get; set; }
}

其中:

Url指定了请求的Url匹配规则,还定义了URL应该如何分割成(tokenized)不同的参数,URL中可替换的参数,是通过 [参数名称] 的句法来定义的。

Defaults属性定义了一个默认值的字典,可以在进来的URL并不包含某个指定的参数值的情形下使用。

RouteHandler属性定义了在URL被分割成参数,适当的路径选择规则被确定之后,应该用来处理请求的 IRouteHandler 实例。

Validation属性允许我们指定一个路径选择规则匹配需要满足的先决条件,如对参数值使用正则表达式,来过滤一个路径选择规则是否匹配。

II6与II7的区别

在使用ASP.NET MVC Framework时,要注意一点II6和II7的区别,如果在II7下,不需要使用.mvc扩展名,路径选择规则可以如下所示:

RouteTable.Routes.Add(
new Route
{
Url = "[controller]/[action]/[id]",
Defaults = new { action = "Index", id = (string)null },
RouteHandler = typeof(MvcRouteHandler)
});

而在II6下,控制器名后面必须要有.mvc扩展名:

RouteTable.Routes.Add(
new Route
{
Url = "[controller].mvc/[action]/[id]",
Defaults = new { action = "Index", id = (string)null },
RouteHandler = typeof(MvcRouteHandler)
});

路径选择规则的验证

在本文第二节我们提到过Route的一个Validation属性,允许我们指定一个路径选择规则匹配需要满足的先决条件。如下代码段所示,验证Id必须为整数且长度在1到8之间:

RouteTable.Routes.Add(
new Route
{
Url = "Blog.mvc/Detail/[id]",
Defaults = new { controller = "Blog", action = "Detail" },
Validation = new { id=@"\d{1,8}" },
RouteHandler = typeof(MvcRouteHandler)
});

自定义RouteHandler

ASP.NET MVC Framework中,提供了很好的扩展功能,如我们可以自定义RouteHandler来实现在Web.config中配置 ControllerFactory和ViewFactory。在ASP.NET MVC Framework中,自定义RouteHandler只需要实现IRouteHandler接口并实现GetHttpHandler方法,它的定义如 下:

public interface IRouteHandler
{
IHttpHandler GetHttpHandler(RequestContext requestContext);
}

Fredrik在它的Blog上写了一个完整的示例,有兴趣的朋友可以参考一下。

在Web.config中定义路径选择规则

ASP.NET MVC Framework中路径选择规则定义在Global.asax中的Application_Start方法中,当映射规则发生改变时,如果修改了 Application_Start中的代码,必将导致整个应用程序的重新编译,我们完全可以通过HttpModule来实现把映射规则放在配置文件中。 如下示例代码所示:

public class RouteBuilder : IHttpModule
{
public void Init(HttpApplication application)
{
RouteConfiguration routeConfig =
(RouteConfiguration)System.Configuration.ConfigurationManager.GetSection("RouteTable");
foreach (RouteElement routeElement in routeConfig.Routes)
{
Route currentRoute = new Route();
currentRoute.Defaults = new DefaultsType(routeElement.Defaults);
currentRoute.Url = routeElement.Url;
currentRoute.RouteHandler = typeof(MvcRouteHandler);
RouteTable.Routes.Add(currentRoute);
}
}
public void Dispose()
{
}
}

在CodePlex上,已经有了做了这样的开源项目,地址为:http://www.codeplex.com/RouteBuilder,有兴趣的朋友参考一下。

结束语

关于ASP.NET MVC Framework中的路径选择就简单的介绍到这儿了,其实本篇文章后两节都是在推荐一些学习的资料,而关于路径选择的详细内容,在ScottGu的文章里已经写的很仔细了。

[MVC]ASP.NET MVC - 旧形式URL的路由

mikel阅读(789)

原文地址:ASP.NET MVC – Legacy Url Routing
原文作者:matthaw

本文地址:ASP.NET MVC – 旧形式URL的路由
本文作者:Q.Lee.lulu

最 近我们已经将大部分我们ASP.NET Web Form页面转换为使用ASP.NET MVC。当这个并不是一个小的工程的时候,使用一个新的URL结构并支持旧的URL是很有需要的。这个想法是,当你点击一个不存在的页面的时候,你会被定 位到MVC里面合适的Controller和Action中去。

流程

image

1. 一个来自你的网站的旧形式的URL请求.例如:http://www.server.com/Users/Login.aspx
2. ASP.NET routing 拦截这个请求并匹配你RouteCollection中的一个Route  
3. 并不是使用 MvcRouteHandler, 而是 LegacyRouteHandler 被调用.
4. 使用 LegacyRouteHandler, 它将会使用你指定的Route重定向名来产生MVC URL,并发出一个定位到http://www.server.com/site/login的HTTP 301 请求.

Routing

首先,我们需要定义我们的旧版Route类。这是必需的,因为我们需要暴露一个额外的属性来允许我们的routing处理器来找到正确的MVC route。

// 旧形式URL的route类,暴露一个RedirectActionName
public class LegacyRoute : Route
{
public LegacyRoute(string url, string redirectActionName, IRouteHandler routeHandler)
: base(url, routeHandler)
{
RedirectActionName = redirectActionName;
}
public string RedirectActionName { get; set; }
}

然后,我们需要定义route handler和关联的http handler。Route handler继承自IRouteHandler,而当创建你的旧形式URL routing的时候要使用这个类。Http handler继承自MvcHandler,因为它会为我们提供一些关键的信息,例如RequestContext。你也许会注意到你需要从 request上copy所有的querystring参数(代码中并没有写出来)。这是必需的一步,因为GetVirtualPath方法调用时会需要 所有的route data(来自RouteData.Values)并尝试利用它来创建URL。

// The legacy route handler, used for getting the HttpHandler for the request   
public class LegacyRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new LegacyHandler(requestContext)
}
}
// The legacy HttpHandler that handles the request
public class LegacyHandler : MvcHandler
{
public LegacyHandler(RequestContext requestContext) : base(requestContext) { }
protected override void ProcessRequest(HttpContextBase httpContext)
{
string redirectActionName = ((LegacyRoute)RequestContext.RouteData.Route).RedirectActionName;
// ... copy all of the querystring parameters and put them within RouteContext.RouteData.Values
VirtualPathData data = RouteTable.Routes.GetVirtualPath(RouteContext, redirectActionName, RouteContext.RouteData.Values);
httpContext.Status = "301 Moved Permanently";
httpContext.AppendHeader("Location", data.VirtualPath);
}
}

最后,你需要在Global.asax文件中创建你的routes。记住,在配置routing的时候这是必需的一步。

public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Login", "site/login", new {
controller = "Users",
action = "DisplayLogin"
});
routes.Add("", new LegacyRoute(
"Users/Login.aspx",
"Login",
new LegacyRouteHandler()
));
}

 

就是这样。当一个请求来到的时候,你会在Fiddler中看到如下的信息:

  1. A request on "Users/Login.aspx"
  2. A HTTP 301, with a header "Location" and value of "site/login"
  3. A request on "site/login"

Final Thoughts

Granted, there's more you can do with this – like creating your own extension methods like MapRoute and doing better handling of finding the route, but this should get you started. Also, I'm writing the code off the top of my head, so there's no guarantee that any of it works as-is. Please let me know if you have any other thoughts.

Lastly, for those wondering why are we using a HTTP 301 status code? Well read up on them. "301 Moved Permanently" indicates "that all future requests should be directed to the given URI." While your end users will not see any difference other than a new URL in the browser, the 3