[转载]庖丁解ASP.NET MVC内部运行流程

mikel阅读(955)

[转载]庖丁解ASP.NET MVC内部运行流程 – Lipu falls in .Net – 博客园.

跟大家分享两张ASP.MVC的内部运行流程图,作者都是Steve Sanderson,上面这张是07年MVC还未正式发布之前做成的流程图,虽然较老但是放到今天大部分仍然基本适用(注意:图中有些部分已经过期但是因为比较详细全面,也贴上来了)。下面那张是09年的,贴上来的理由是图中详细的表出MVC框架里哪些部分是可以被拓展(extend)的, 比如说你可以用Cusotom route handler, Custom Handler 等等。

基本上讲,一个MVC项目启动时,包括下面5个步骤:

1. 启动:一个MVC的项目启动的时候,首先从Global.asax.cs里面的Application_Start()开始。在 Application_Start()里面定义routes, IoC container, ModelBinder, 等等需要在启动时配置好的部分;

2. 路径(routes):MvcHandler处理所有的请求 e.g. http://example.com/home/index,在这个环节的最后一步IHttpHandler的ProcessRequest方法被调用;

3. 生成controller: 在调用ProcessRequest方式时,DefaultControllerFactory按照进来的请求生成controller对象,e.g. HomeController. 之后controller对象调用Execute()方法;

4. 找到并调用controller action:controller在调用Execute()方法时,依靠ControllerActionInvoker找到相应的action,并调 用该action, e.g. 调用HomeController里面的index()方法;

5. 生成界面(render view): 最后controller返回ActionResult,由View Engine生成界面;

Creative Commons License

[转载]与VisualStudio集成的若干种代码生成解决方案

mikel阅读(1295)

[转载]与VS集成的若干种代码生成解决方案[博文汇总(共8篇)] – Artech – 博客园.

前一阵子写了不少关于代码生成相关的文章,介绍了一些如何通过VS自动生成代码的解决方案,比如CodeDOM、T4以及ASP.NET的BuildProvider等。现在将它们作一个汇总,给广大读者作一个参考。

[第1篇] 通过CodeDOM定义生成代码的结构

我 不知道大家对CodeDOM的代码生成机制是否熟悉,但是有一点可以确定:如果你使用过Visual Studio,你就应该体验过它带给我们在编程上的便利。随便列举三种典型的代码生成的场景:在创建强类型DataSet的时候,VS会自动根据 Schema生成相应的C#或者VB.NET代码;当我们编辑Resource文件的时候,相应的的后台代码也会自动生成;当我们通过添加Web Reference调用Web Service或者WCF Service的时候,VS会自动生成服务代理的代码和相应的配置。总的来说,通过和VS集成的动态代码生成工具使我们可以“强类型”的方式进行编程,进 而提供我们的效率并减低错误的几率。

CodeDOM 提供了表示许多常见的源代码元素类型的类型。您可以设计一个生成源代码模型的程序,使用CodeDOM 元素构成一个对象图。而这个对象图包含C#或者VB.NET代码包含的基本元素:命名空间、类型、类型成员(方法、属性、构造函数、事件等),并且包括方 法实现的具体语句(Statement)。也就是说它的结构就是对一个具体.vb或者.cs文件代码的反映。在这里我不会具体介绍CodeDOM体系结 构……[阅读全文]

[第2篇] 通过Visual Studio的Custom Tool定义代码生成器

在《前篇》中我们已经通过CodeDOM的形式定义了将要生成的代码结构,而这个CodeDOM对象就是CodeCompileUnitCodeCompileUnit本身是与编程语言无关的,我们可以利用基于某种编程语言(VB.NET或者C#)的CodeDomProviderCodeCompileUnit对 象转换成具体的代码文本。为了让我们的VS自动地为我们生成代码,我们需要以Custom Tool的形式编写相应的代码生成器。我们编写的代码生成器最终通过COM组件的形式对外提供服务,所以其中涉及到COM组件的注册。为了实现同VS的集 成,还涉及到相应注册表设置……[阅读全文]

[第3篇] 不同于CodeDOM的代码生成机制——T4

前 面两篇介绍了如何通过CodeDOM+Custom Tool的代码生成方式实现了代码的自动生成。实际上,我们最常用的代码生成当时不是CodeDOM,而是T4,这是一个更为强大,并且适用范围更广的代 码生成技术。T4是对“Text Template Transformation Toolkit”(4个T)的简称。T4直接包含在VS2008和VS2010中,是一个基于文本文件转换的工具包。T4的核心是一个基于“文本模板”的 转换引擎,我们可以通过它生成一切类型的文本型文件,比如我们常用的代码文件类型包括:C#、VB.NET、T-SQL、XML甚至是配置文件等。

对 于需要通过T4来进行代码生成工作的我们来说,需要做的仅仅是根据转换源(Transformation Source),比如数据表、XML等(由于例子简单,HelloWord模板没有输入源)和目标文本(比如最终需要的C#或者T-SQL代码等)定义相 应的模板。T4模板作用就相当于进行XML转化过程中使用的XSLT。

T4模板的定义非常简单,整个模板的内容包括两种形式:静态形式 和动态动态。前者就是直接写在模板中作为原样输出的文本,后者是基于某种语言编写代码,T4引擎会动态执行它们。这和我们通过内联的方式编写的 ASP.NET页面很相似:HTML是静态的,以C#或者VB.NET代码便写的动态执行的代码通过相应的标签内嵌其中。[阅读全文]

[第4篇] 通过T4模板实现单文件的代码生成

在《前一篇》 中,我对T4模板的组成结构、语法,以及T4引擎的工作原理进行了大体的介绍,并且编写了一个T4模板实现了如何将一个XML转变成C#代码。为了让由此 需求的读者对T4有更深的了解,我们通过T4来做一些更加实际的事情——SQL Generator。在这里,我们可以通过SQL Generator为某个数据表自动生成进行插入、修改和删除的存储过程……[阅读全文]

[第5篇] 通过T4模板实现多文件的代码生成

在《前一篇》中我们通过T4模板为我们指定的数据表成功生成了我们需要的用于添加、修改和删除操作的存储过程。但是这是一种基于单个文件的解决方案,即我们必须为每一个生成的存储过程建立一个模板。如果我们提供一种基于多文件的代码生成方式,将会为编程人员带来极大的便利。借助于T4 ToolBox这个开源工具箱,多文件的SQL Generator的实现变得异常简单。[阅读全文]

[第6篇] 解决T4模板的程序集引用的五种方案

如 果你的T4模板需要调用一个自定义的类型,并且该类型定义在某个非系统程序集中,你就需要通过T4<#@ assembly…#>指令引用该程序集。在VS 2008环境下,你只需要为当前项目添加相应的程序集引用,并且通过<@ assembly…#>指令的name属性指定程序集(Dll)的文件名即可。但是这种方式对于VS 2010则行不通,因为T4引擎在解析和编译模板的时候并不会从项目引用列表中去定位目标程序集。本篇文章为你介绍在VS2010下5种不同的程序集引用 的方式……[ 阅读全文]

[第7篇] 编写T4模板进行代码生成无法避免的两个话题:”Assembly Locking”&”Debug”

出 于提高性能考虑,T4引擎在进行基于代码生成的模板转换(Template Transformation)的时候,会始终重用同一个AppDomain。由于该AppDomain不会自动卸载,这就会导致该AppDomain始 终锁定所有被它加载的程序集。如果我们需要释放程序集,我们不得不重启VS。但是,对于T4模板的开发调试阶段,这种通过重新启动VS的方式去释放程序集 以确保我们的项目能够成功编译是不能接受的。那么,是否有一种解决方案既能够确保T4引擎能够进行正常的模板转换,又能避免它强行锁定引用程序集呢?

VS和一些T4编辑器虽然给了基本的智能感知支持,但是在绝大部分我们相当于在编写纯文本的脚本,所以对于一些比较复杂的模板转换逻辑,我们需要通过Debug的方式去发现一些无法避免的问题……[阅读全文]

[第8篇] 通过自定义BuildProvider为ASP.NET提供代码生成

对 于ASP.NET应用的开发者来说,你可能不知道什么是BuildProvider,但是你几乎无时无刻不在使用它所带来的代码生成机制。当你创建一 个.aspx文件的时候,为什么会自动创建对应源代码?当你在该.aspx页面中以XML的方式添加一个按钮,源代码中为什么会自动添加一个同名的属性。 实际上,ASP.NET就是通过一个特殊的BuildProvider实现了将.aspx文件内容转换成相应的源代码,这个特殊的.aspx文件就 是:PageBuildProvider。基于不同的文件类型,ASP.NET会采用不同的BuildProvider进行源代码的生成。比如 UserControlBuildProvider和MasterPageBuildProvider分别实现了基于用户控件文件(.ascx)和母板页 (.master)的源代码生成……[阅读全文]

[转载]ASP.NET MVC3 On Razor使用(2):自定义Helper方法

mikel阅读(997)

[转载]MVC3 On Razor使用(2):自定义Helper方法 – 信心、恒心、决心 – 博客园.

在使用自定义Helper方法时,开始我试了很多次都不成功一直报错“找不到WebMatrix.WebData.dll、WebMatrix.Data.dll”。

应该是一个bug,估计正式版不会有这个问题。解决方案是:

在使用自定义Helper时,需要引用默认C盘路径下的WebMatrix.WebData.dll、WebMatrix.Data.dll类库并Copy到本地,否则会出错。

clip_image002

如上图所示,首先在网站根目录下添加一个“App_Code”文件夹,并创建一个Razor的ViewPage为MyHelpers.cshtml。代码如下图所示:

clip_image004

通过@helper{}声明就可以定义一个Helper辅助函数,上述函数根据姓名循环输出一个列表。这种html和cs混合输出的helper相当简洁,相对于MVC2

写一个helper容易多了。

下面在页面中调用,只要一行代码就可以了:

clip_image006

同样可以在其他页面调用,输出结果如下:

image

注意的是,写法上将MyHelpers文件名加上,如果有文件夹则文件夹名字也要加上。使用规则是@FolderName.FileName.HelperName

[转载]ASP.NET MVC3 On Razor使用(1):基础部分

mikel阅读(1032)

[转载]MVC3 On Razor使用(1):基础部分 – 信心、恒心、决心 – 博客园.

微软在10号发布了ASP.NET MVC3的发布候选版(RC:Release-candidate),该版本提供了Razor视图引擎的智能感知等功能,并同样具备Online许可。

特意下载安装了下,下载地址:http://www.microsoft.com/downloads/en/details.aspx?FamilyID=a920ccee-1397-4feb-824a-2dfefee47d54&displaylang=en

试用了传说中强大的Razor引擎,实在是太棒了。下面是我的一些使用学习体会:

一、Razor基础简介

Razor采用了cshtml后缀的文件名,截图如下:

clip_image002

A、 版面布局

从图上看到,新的视图引擎已经没有了Site.Master这种MasterPage了,取而代之的是_Layout.cshtml和_ViewStart.cshtml。

_Layout.cshtml等同于MasterPage;如下图所示:

clip_image004

这个_Layout.cshtml文件中已没有了MasterPage中<asp:ContentPlaceHolder ID=”MainContent” runat=”server” />的标签语句了,取而代之的是

@RenderBody():呈现子页的主体内容

@RenderSection():呈现特别的节部分。

HelperResult RenderSection(string name, bool required = true);

required默认为true必须覆写,设为false则为可选覆写;

注意的是:该函数在RC版中参数有所改变,参数中optional改为required,据说和VB的关键字冲突

下图则为我在子页的页脚部分覆写,在子页实现时,使用@section 自定义节名{ }格式。

clip_image006

如果我们想在父页里写一个默认实现节,在子页作为可选实现该怎么做呢?

这个在MasterPage是直接支持的,但在Razor里就要稍显复杂些,个人觉得这种写法还可以改进下;

如下图所示,我在父页写了一个默认的页脚实现,如果子页有实现则使用子页实现,没有就用默认实现。

这里要先通过bool IsSectionDefined(string name)函数判断是否有子页实现,有则调用子页实现。

clip_image008

_ViewStart.cshtml:该页面可以理解为其它View的基类。它默认的代码就3行如下所示:

clip_image010

在该页面里定义了其它View的默认MasterPage是“~/Views/Shared/_Layout.cshtml”;

当然在这里还可以定义一些公用的函数及变量,以供其它页面可以直接调用。

B、与ASPX语法的区别

ASPX:<% %>

Razor:@{ }

这两种都是代码块的编写符号,只不过用Razor会让代码更加简洁和舒适,下图为对比语法:

clip_image002[4]

ASPX:<%= %>

Razor:@
在Razor中,你只需要用一个”@”字符就可以标识代码块的开始,它能够自动判断后面变量的结束位置,如下图对比看出,使用Razor语法,少写了不少代码。

clip_image004[5]

clip_image006[4]

而且,Razor解析器内置了很多语言的智能.
大部分情况下,Razor解析器都有足够的能力推导出模板里的一个“@”字符到底是在代码中用到,还是在静态内容中用到。

例如,我在下例中的邮件地址中使用了”@”字符,则scottgu@microsoft.com被解析为静态内容,而@DateTime.Now被解析代码执行了,很强大吧!
如果碰到和代码一样格式的内容(或者你想把代码当作内容看待),你可以显式地打@@来用另外一个”@”字符进行转义。

image

ASPX:<%: %>

Razor:@()

这两种语法都能实现内容的编码,只不过Razor更为方便一些,如下图所示:

clip_image008[4]

clip_image010[4]

结果输出:<span>文本编码</span>

C、其他的一些Razor语法

image
如上图所示,
@using :导入命名空间
@model:声明页面接收的Model
@section:定义要实现父页的节信息
这里列出的只是一些简单常用的,需要更多语法可以参考API文档。

二、MVC3 RC特性展示

相比较MVC2,版本3还是有很大的改进(除了Razor引擎)。

A、Partial Page Output Caching(部分页输出缓存)

ASP.NET MVC在第一版的时候就开始支持整页缓存。从ASP.NET MVC V3开始,我们也可以支持部分页缓存了。

这可以很容易的使你部分缓存或者一个response的片段。

例如我们要根据一个类别获取产品列表,当请求类别相同时,就输出缓存的产品列表。

如下图所示,Action上定义了输出缓存的过期时间1小时,缓存参数为category。

因此当发生重复请求时,就可以自动输出缓存信息避免频繁查询数据库,减轻服务器负担和提高响应速度:

image

image

B、隐式JavaScript和验证

其中一个比较出色的ASP.NET MVC3的改进是AJAX 和 Validation helpers将会默认使用unobtrusive JavaScript,ajax采用了JQuery来编写。

image

image

默认配置节里启用了unobtrusive JavaScript,故将上述js开启后,则验证就为客户端验证。

相比MVC2的ajax验证,不需要在View页里添加<%Html.EnableClientValidation(); %>这行代码了。

C、全局过滤器

ASP.NET MVC 支持通过过滤机制来描述性地应用“横切”逻辑。 你可以使用属性语法为控制器和执行函数指定过滤器,如下所示

image

但在使用中,我们常常希望将一些过滤器逻辑应用于程序中的所有控制器上,如Authorize过滤器。

现在ASP.NET MVC3 能够让你指定一个全局的过滤器,这个过滤器可以应用于程序中的所有控制器上。

如下图所示:在Global文件中将自定义过滤器加入GlobalFliterCollection中就可以了

image

在MVC3中,这个过滤器的判定逻辑非常灵活,你可以配置一个全局过滤器,使它只在某些条件符合的时候才启用。

如下图所示,我实现了一个全局权限过滤器,在任何页面如果没有登录的话则跳转到LogOn页面。因为是全局的,当跳转到LogOn页面时,

也要判断是否有权限,所以最终页面上什么都不显示。

那如何解决这个问题呢,让LogOn页面不应用该全局权限呢?

如下图所示:我们实现一个IFilterProvider,在里面过滤掉LogOn页面而应用于其他的页面。代码如下:

image

在使用Authorize过滤器时,发现了一个bug,就是当没有权限时,系统会自动跳到“Account/LogIn”页面,而不是跳转到webconfig中设置的页面。

<authentication mode=”Forms”>
<forms  loginUrl=”~/Account/LogOn” timeout=”2880″ />
</authentication>

这个设置好像不起作用了,通过查看ScottGu’s Blog,知道这是一个bug,解决方案就是在Configuration配置节下加入

<appSettings>
<add key=”autoFormsAuthentication” value=”false” />
</appSettings>

我想在正式版中不会再有这个问题了。

还有其他的一些新的特性,可以参考http://tech.it168.com/a2010/1111/1124/000001124749_1.shtml

[转载]在MVC2.0 中 遭遇无法被 Try Catch 的 “Exception”

mikel阅读(1105)

[转载]在MVC2.0 中 遭遇无法被 Try Catch 的 “Exception” – RyanDing – 博客园.

前天当我为新项目新增完日志模块后对日志模块进行测试,测试时居然发现开发人员一段非常简单的代码,而且很标准的try … catch .. 写法。代码如整理如下:

1 public JsonResult SaveTest() 2 { 3 try 4 { 5 //LinqToSql:返回IQueryable数据集合。 6   var iQueryableData = (from o in _Context.Orders//.Where(o => o.OrderID == 10248) 7 select new 8 { 9 ShipName = o.ShipName, 10 Employee = o.Employee, 11 }).ToList(); 12 13 //LINQ:返回IEnumerable集合。 14 var iEnumerableData = from d in iQueryableData 15 select new 16 { 17 ShipName = d.ShipName, 18 EmployeeName = d.Employee.LastName //空引用未处理引发程序异常。 19 }; 20 21 return Json(new { Success = true, Msg = iEnumerableData }, JsonRequestBehavior.AllowGet);   22 } 23 catch (Exception ex) 24 { 25 return Json(new { Success = false, Msg = ex.Message }, JsonRequestBehavior.AllowGet); 26 } 27 }

为方便大家阅读,我用 NORTHWIND 数据库。同时在该数据库内执行 SQL :update orders set EmployeeID =null where OrderID =10248 。这样造成上述代码第18 行Linq代码迭代时产生异常。您认为 这个异常可以被catch住么?答案当然是否定的!

当发现这个Bug后顿时让我产生了兴趣,我不知道如果微软的MVC项目开发人员看到这个bug,他是如何解释的。接下来让我就来一个非官方解释吧,欢迎拍砖!

我们可以在第18行设置好断点,然后用VS2010 Debug程序,发现执行到第 21 行 return Json 时 VS未报任何bug。过数秒后断点直接定位在第18行,提示也很简单:未将对象应用到实例。这个确实是我们预期想要的 Exception 但是它始终没有被 catch 住!通过StackTrace我发现了这么一句提示: at System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) 。上述代码第21行return Json 被执行后返回了 JsonAction 这个 JsonAction 被 InvokeActionResult 调用?难道这个就是问题的关键所在?原因是写有try catch 函数(本例的SaveTest()函数)的外层调用函数(InvokeActionResult()函数)出现了异常。也就是外层函数出现异常我们的内层 函数SaveTest()又怎能try catch 到这个外层异常呢?于是通过Reflector进一步证实我的想法。具体的请看下图:

主要是这句action.ExecuteResult(ControllerContext);它的执行过程如下:

最后一幅图的JavaScriptSerializer serializer = new JavaScriptSerializer(); 进行 json 序列化时这个外层函数报出了异常。这个异常在我们的内层函数 SaveTest() 方法内是不可能被截获到的。此刻我已完全弄明白这个无法被catch 的exception 是怎么出现的!至于这个异常为什么会被外层函数触发,主要是.net Linq 延迟加载机制导致的,这不属于本文讨论的范畴,具体的园子内很多文章已经解释的很清楚了。

为消灭掉这个截获不到的exception现附上解决方案:

MVC虽然很年轻,但是这个架构真的很优秀我们可以非常方便对其扩展(我不是MVC的托,呵呵)。为了解决问题,我们只要自定义一个特性该特性继承 HandleErrorAttribute  特性即可。接着重写基类的 OnException 虚方法。代码如下:

CustomHandleErrorAttribute

public class CustomHandleErrorAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext Context) { base.OnException(Context); dynamic ex = Context.Exception; if (!Context.ExceptionHandled) return; //TODO:将 ex 错误对象记录到系统日志模块 } }

接着我们在MVC Controller 内这样调用:

CustomHandleError调用

/* *截获InvokeActionResult 调用 actionResult参数的 ExecuteResult 方法。 ExecuteResult 方法执行时 进行 以下操作: JavaScriptSerializer serializer = new JavaScriptSerializer(); response.Write(serializer.Serialize(this.Data)); 将C# 匿名对象序列化成json数据供 jquery ajax 方法回调。 */ [CustomHandleError] public JsonResult SaveTest() { //try //{ //TestMehod(); //LinqToSql:返回IQueryable数据集合。 var iQueryableData = (from o in _Context.Orders//.Where(o => o.OrderID == 10248) select new { ShipName = o.ShipName, Employee = o.Employee, }).ToList(); //LINQ:返回IEnumerable集合。 var iEnumerableData = from d in iQueryableData select new { ShipName = d.ShipName, EmployeeName = d.Employee.LastName //空引用未处理引发不可截获的异常。 }; return Json(new { Success = true, Msg = iEnumerableData }, JsonRequestBehavior.AllowGet); //Json序列化。 //} //catch //外层错误,导致内层函数catch失效,无法有效的截取错误信息。 //{ // return Json(new { Success = false, Msg = "操作失败。" }, JsonRequestBehavior.AllowGet); //} }

代码看上去更简洁了吧?连try catch 都不用写了更不用担心啥时候这个截获不到的exception神秘人物登场,这样噩梦结束了。。。

本例代码这里下载

[转载]CMS系统模板引擎设计(5):Label应用初探

mikel阅读(926)

[转载]CMS系统模板引擎设计(5):Label应用初探 – 氣如蘭兮長不改,心若蘭兮終不移。 – 博客园.

话说上节 听的很郁闷,讲的也郁闷,整个系列没有详细的Code,所以都有点懵。其实仔细看之前的几节应该还是能够理解整个思路的。 我在这里再整理一遍: 用户访问URL后 ->根据所有Page的URL规则(urlpattern)获取当前Page,然后再根据page的Template,Find出所有的 Label(通过反射),然后遍历这些Label,把得到的数据的Html替换掉Label本身的标签代码。最后整个Template就是要生成的 HTML了。
所以我们 要明白Label是如何获取工作才能获取数据库的数据的。一个Label可以理解成一个控件,那么控件必然会支持一些属性(Parameter)和一些内 容(Field)。 我们上节就是在讲怎么来解析parameter,因为有些特殊的parameter,所以设计的时候设计了parameter的基类,特殊的则是子类。
同样,field是具体的要展现在HTML代码中的字段,比如中的[field:Title/],这就是一个字段,我们的模版引擎的工作就是把他替换掉应该展现的标题,而如何才能工作?我们就得设计Field的整个逻辑。在替换循环的过程中使用field类。
但是,我今天不讲Field了,因为这样讲大家还是糊涂依旧。今天我们就来设计一个Article:List的初级版。我觉得或许从实例讲解大家更容易理解设计的理念。OK,那就开始设计一个Article.List,我们最熟悉的文章列表。

//简单的循环列表
{Article:List Top=”10″ CategoryId=”5″}
<a href =”/details/[field:FileName/]” target=”_blank”>[field:Title/]</a>

{/Article:List}

想象一下Repeater,有个ItemTemplate,那么对于List这个Label来说,他的ItemTemplate显然就是Template属性。那么如果我们获取到数据源后直接foreach替换掉所有的field即可。代码大概如下:
/// <summary>
/// 获取要展示的HTML代码
/// </summary>
/// <returns></returns>
public override string GetRenderHtml()
{
var html
= TemplateString;
foreach (var article in GetDataSource())
{
foreach (var field in Fields)
{
html
= html.Replace(field.Html, field.GetValue(article));
}
}
return html;
}

从上面的方 法中,我们可以看到替换的机制是每一行数据都要执行一次所有字段的替换(所以之前有提过在构造嵌套的时候为了防止Field混乱要处理 TemplateString),最后返回html。我们还能看到一些未知的方法和字 段:GetDataSource(),Field.Html,Field.GetValue(),这些已经暴露了我们的Field设计的部分内容。我们先 看GetDataSource()是什么?

代码

/// <summary>
/// 获取Article列表
/// </summary>
/// <returns></returns>
private IEnumerable<Article> GetDataSource()
{
var parameter
= new ArticleQueryParameter();
//构造查询的参数

return ArticleDataHelper.GetList(parameter);
}
/// <summary>
/// 查询参数基类
/// </summary>
public class QueryParameter
{
public int PageSize { get; set; }
public int PageIndex { get; set; }
public int RecordCount { get; set; }
public string SearchKey { get; set; }
}
/// <summary>
/// 文章查询类
/// </summary>
public class ArticleQueryParameter
{
public QueryParameter PageParameter { get; set; }
public DateTime PostTime { get; set; }
public int CategoryId { get; set; }
public int Top { get; set; }
}
/// <summary>
/// 文章获取数据类
/// </summary>
public class ArticleDataHelper
{
public static IEnumerable<Article> GetList(ArticleQueryParameter parameter)
{
return null;
}
}

其 实就是获取ArticleList的数据源,具体的实现方式大家可能都不一样,但Article.List最终需要这么一个数据获取的方法,然而这个方法 都需要接受一些查询条件的参数,这些参数都来自Parameters!!现在我们来填充GetDataSource()的参数构造部分。

private IEnumerable<Article> GetDataSource()
{
var parameter
= new ArticleQueryParameter();
//构造查询的参数
parameter.CategoryId = Parameters[CategoryId].ConvertToInt(0);
parameter.Top
= Parameters[Top].ConvertToInt(Parameters[PageSize].ConvertToInt(0));
var pageIndex
= Parameters[PageIndex].ConvertToInt(1);
if (pageIndex > 1)
{
parameter.PageParameter
= new QueryParameter
{
PageIndex
= pageIndex,
PageSize
= parameter.Top
};
}
return ArticleDataHelper.GetList(parameter);
}

Parameters 是Label的ParameterCollection,他可以通过索引直接访问具体的parameter。ConvertTo<T>(T defaultValue)是可以将parameter的value转成T类型。 这就是Parameter所用到的地方之一。另外可以看到Field具体Html属性和GetValue方法,而且GetValue接受了当前 Article实体作为参数(不接受参数的话,我们怎么得到某个字段的值呢:)。

整 个List流程应该比较清楚了吧,获取数据源,然后循环数据,每行再去替换所有的Field,最后把拼接好的HTML返回。当然这是List,如果是其他 的标签可能就是另外一个处理办法。比如System.Include标签,他的工作就是嵌入一个用户控件(PartialTemplate),那么他的处 理逻辑和List就完全不一样(他是先根据templateid参数的值获取template,然后再把自己所有的Parameters传递给这个 template里的所有标签,最后再把这个template替换后的结果作为自己的结果返回,他没有循环)。所以我们的具体控件逻辑都是大相径庭的,但 最终都是要返回替换后的HTML,但所有的List却都是差别多的,无非就是不同的数据源进行循环。所以对于List我们应该进行抽象,把公共部分提取出 来,尽量让每个具体的Label更明确职责。如何抽象呢? 那就看看有没有可提取的公共部分。

所 有的List都可能会有分页,所以ListBase应该有PageParameter,所有的List都会去循环DataSoruce,所以 ListBase默认实现了DataSource循环,但是增加了一个方法那就是GetDataSource。这个方法是抽象的,所有的List必须实 现。

代码

/// <summary>
/// 循环标签基类
/// </summary>
public abstract class ListBase : Label
{
public QueryParameter PageParameter { get; set; }

public abstract IEnumerable<dynamic> GetDataSource();

public override string GetRenderHtml()
{
var dataSource
= GetDataSource();
if (dataSource == null) return string.Empty;
var html
= TemplateString;
foreach (var dataItem in dataSource)
{
foreach (var field in Fields)
{
field.Data
= dataItem;
html
= html.Replace(field.Html, field.GetValue());
}
}

return html;
}
}

foreach里我也做了点细微的调整,就是把Field的GetValue的参数拿掉了,换成了成员,这样更明白些。你可能会有一些疑点:

为什么设计为抽象而不是虚方法或接口?

所有子类的实现方法都不一致,没有可提取部分,所以虚函数没有意义,如果单独抽象成接口,则所有子类必须继承此接口,因为GetRenderHtml和该方法紧密结合,foreach里需要显式转换为接口才能调用,完全没有意义。

为什么是GetDataSource方法,而不是公开一个DataSource成员? 如果需要Set呢?还要增加一个SetDataSource?

其 实这个我考虑过,很少有Set的情况,因为标签都是自动生成的没有外部去干扰(Set),但不能否认以后完全没有,如果设为成员,则必须有一个可get的 地方,要么是abstract,那样也会把set abstract,要么就在Init里给set先,那也得有一个抽象的set方法。所以考虑现状还是使用一个方法最为合适。

另外一点就是为什么用了dynamic,而不是T。

首 先不能是T,如果是T,则GetRenderHtml调用时也需要指明T,则整个ListBase就要变成泛型类ListBase<T>,除 非base不执行GetDataSource调用。为什么不能用ListBase<T>?因为有些GetDataSource会用linq返 回匿名类型集合,子类无法确定返回的具体类型名称,所以就不能继承ListBase<T>。但我们可以用dynamic,动态类型,到真正执 行时可以确定T就行,这个不用我们操心,然而object显然略逊一筹了。

这样一来,Article的List只需要实现GetDataSource就行了。

这只是最简单的List雏形,假如说我还需要像Repeater控件那样,有headtemplate itemtemplate foottemplate altertemplate spacetemplate怎么办?

这 个就需要定义子标签类了。这里我就不多说了,其实很简单,就是再定义几个Label,他们又各自的获取Html的方法,我们最后组合起来就行。自需要注意 List的Template和Field已经没了,都属于子标签了。而且像交替执行的(Item和Alter)需要再循环里给他们隔行赋值。下面是我以前 写的代码,虽然比较难看,不太OO,但能说明实现的逻辑:

代码

public class ListLabelBase : LabelBase
{

public LabelHtmlAttrs HtmlAttrs { get; set; }

public PageParameter Page { get; set; }

public ListTemplate ItemTemplate { get; set; }
public ListTemplate AlterTemplate { get; set; }
public ListTemplate HeadTemplate { get; set; }
public ListTemplate FootTemplate { get; set; }
public ListTemplate NullTemplate { get; set; }
public SpaceTemplate SpaceTemplate { get; set; }

public int Rows { get; set; }

public override void Init()
{
HtmlAttrs
= new LabelHtmlAttrs(Attrs);
Rows
= Attrs.GetAttribute(Rows, 0);
InitTemplates();
Page
= new PageParameter
{

PageIndex = Attrs.GetParameter(PageIndex, 1),
PageSize
= Attrs.GetAttribute(PageSize, Rows),
OrderBy
= Attrs.GetAttribute(Sort, string.Empty),
IsASC
= Attrs.GetAttribute(asc, false)
};

Fields.SetAllBeginReplace((field, obj) =>
{
if (obj != null)
{
var tmp
= ((ListItem)obj).Item.GetPropertyValue(field.Name);
field.Value
= tmp == null ? string.Empty : tmp.ToString();
}
});

Fields[ItemIndex].SetBeginReplace((field, obj) =>
{
field.Value
= obj == null ? string.Empty : (((ListItem)obj).Index).ToString();
});

//实例化PageField
Fields.OverrideField(Page, page => new PageField
{
GetRecordCount
= () => Page.RecordCount,
PageSize
= Page.PageSize,
PageIndex
= Page.PageIndex,
Attrs
= page.Attrs,
Template
= page.Template
});
}

public virtual void InitTemplates()
{
var matches
= Regexs.ListTemplatePattern.Matches(Template);

foreach (Match m in matches)
{
var template
= m.Groups[template] == null ? string.Empty : m.Groups[template].Value;
var value
= m.Groups[value] == null ? string.Empty : m.Groups[value].Value;
var html
= m.Groups[0].Value;
if (string.IsNullOrEmpty(html))
continue;
switch (m.Groups[name].Value)
{
case item:
ItemTemplate
= new ListTemplate { Content = template, Html = html };
break;
case alter:
AlterTemplate
= new ListTemplate { Content = template, Html = html };
break;
case space:
SpaceTemplate
= new SpaceTemplate { Content = template, Html = html, Length = value.ToInt(1) };
break;
case head:
HeadTemplate
= new ListTemplate { Content = template, Html = html };
break;
case foot:
FootTemplate
= new ListTemplate { Content = template, Html = html };
break;
case null:
NullTemplate
= new ListTemplate { Content = template, Html = html };
break;
}
}

if (ItemTemplate == null && !String.IsNullOrEmpty(Template))
{
ItemTemplate
= AlterTemplate ?? new ListTemplate { Content = Template, Html = Template };
return;
}
}

/// <summary>
/// 获取替换后的Html
/// </summary>
public virtual string GetListContent<T>(IEnumerable<T> dataSoruce)
{
if (dataSoruce == null || dataSoruce.Count() == 0)
return string.Empty;
var list
= dataSoruce.ToList();
var firstItem
= list.Count == 0 ? default(T) : list[0];

#region HeadTemplate
if (HeadTemplate != null)
{
var headContent
= ReplaceTemplate(HeadTemplate.Content, new ListItem { Index = 0, Item = firstItem });
Template
= Template.Replace(HeadTemplate.Html, headContent);
}
#endregion

#region FootTemplate

if (FootTemplate != null)
{
var footContent
= ReplaceTemplate(FootTemplate.Content, new ListItem { Index = 0, Item = firstItem });
Template
= Template.Replace(FootTemplate.Html, footContent);
}

#endregion

#region ItemTemplate
if (ItemTemplate != null)
{
//替换循环
var listContent = string.Empty;
if (list.Count > 0)
{

var itemIndex = (Page == null ? 0 : (Page.PageIndex 1) * Page.PageSize) + 1;
if (HtmlAttrs.Cols > 1)
{

listContent += HtmlAttrs.PanelBegin;
var rowNumber
= 0;
foreach (var model in list)
{
var tmpTemplate
= ItemTemplate.Content;
if (AlterTemplate != null && rowNumber % 2 == 0)
tmpTemplate
= AlterTemplate.Content;

listContent += HtmlAttrs.RowBegin;
for (var c = 0; c < HtmlAttrs.Cols; c++)
{
listContent
+= HtmlAttrs.CellBegin;
listContent
+= ReplaceTemplate(tmpTemplate, new ListItem { Item = model, Index = itemIndex });
listContent
+= HtmlAttrs.CellEnd;
itemIndex
++;
}
listContent
+= HtmlAttrs.RowEnd;
rowNumber
++;
}
listContent
+= HtmlAttrs.PanelEnd;
}
else
{
foreach (var model in list)
{
var tmpTemplate
= ItemTemplate.Content;
if (AlterTemplate != null && itemIndex % 2 == 0)
tmpTemplate
= AlterTemplate.Content;

listContent += ReplaceTemplate(tmpTemplate, new ListItem { Item = model, Index = itemIndex });

if (SpaceTemplate != null && itemIndex % SpaceTemplate.Length == 0)
listContent
+= SpaceTemplate.Content;
itemIndex
++;
}
}
if (NullTemplate != null)
Template
= Template.Replace(NullTemplate.Html, string.Empty);
}
else
listContent
= NullTemplate == null ? string.Empty : NullTemplate.Content;

Template = Template.Replace(ItemTemplate.Html, listContent);
}

#endregion

if (NullTemplate != null)
Template
= Template.Replace(NullTemplate.Html, “”);

return Template;
}
}

public class ListItem
{
public object Item { get; set; }
public int Index { get; set; }
}

今天就讲到这了,不知道还有朋友有兴趣没有,目前还没有演示,或许某天我会放出个demo源码。 下次讲Field的设计吧,这也算是最后一个设计了。

[转载]hashtable详细介绍

mikel阅读(863)

[转载]hashtable详细介绍 – YangLei’s – 博客园.

Hashtable的定义

表示键/值对的集合,这些键/值对根据键的哈希代码进行组织。

Hashtable存储结构如下

Hashtable是非泛型的集合,所以在检索和存储值类型时通常会发生装箱与拆箱的操作。

当把某个元素添加到 Hashtable 时,将根据键的哈希代码将该元素放入存储桶中,由于是散列算法所以会出现一个哈希函数能够为两个不同的键生成相同的哈希代码,该键的后续查找将使用键的哈希代码只在一个特定存储桶中搜索,这将大大减少为查找一个元素所需的键比较的次数。

Hashtable 的加载因子确定元素与Hashtable 可拥有的元素数的最大比率。加载因子越小,平均查找速度越快,但消耗的内存也增加。默认的加载因子 0.72通常提供速度和大小之间的最佳平衡。当创建 Hashtable 时,也可以指定其他加载因子。

元素总量/ Hashtable 可拥有的元素数=加载因子

当向 Hashtable 添加元素时,Hashtable 的实际加载因子将增加。当实际加载因子达到指定的加载因子时,Hashtable 中存储桶的数目自动增加到大于当前 Hashtable 存储桶数两倍的最小素数。

扩容时所有的数据需要重新进行散列计算。虽然Hash具有O(1)的数据检索效率,但它空间开销却通常很大,是以空间换取时间。所以Hashtable适用于读取操作频繁,写入操作很少的操作类型。

代码一、

static void Main(string[] args) { Hashtable hashtb = new Hashtable(); hashtb.Add(1, "aa"); hashtb.Add(2, "bb"); hashtb.Add(3, "cc"); hashtb.Add(4, "dd"); foreach (DictionaryEntry item in hashtb) { Console.WriteLine(item.Value); item.Value = "ee"; } Console.Read(); }

编译出错:item为foreach的迭代变量,无法修改其成员。

原因:如果运行foreach处理语句试图修改迭代变量值,或将变量值作为ref参数或out参数传递,那么都会发生编译错误,迭代变量相当于一个局部只读变量。

代码二、

item.Value = “ee”;改成hashtb[item.Key] = “ee”;

运行报错:集合已修改;可能无法执行枚举操作。

原因:.NET Framework 提供枚举数作为循环访问一个集合的简单方法。枚举数只读取集合中的数据,无法用于修改基础集合。

foreach 语句用于循环访问集合,以获取您需要的信息,但不能用于在源集合中添加或移除项,否则可能产生不可预知的副作用。如果需要在源集合中添加或移除项,请使用 for 循环。

代码三、

Thread tr1 = new Thread(new ThreadStart(() => { foreach (DictionaryEntry item in hashtb) { Console.WriteLine(item.Value); } })); tr1.Start(); Thread tr2 = new Thread(new ThreadStart(() => { for (int i = 1; i < 4; i++) { hashtb[i] = "ee"; Console.WriteLine(hashtb[i]); } })); tr2.Start();

线程tr1用来读hashtable,线程tr2用来foreach来枚举修改hashtable,

运行时错误:集合已修改;可能无法执行枚举操作。

代码四、

//读取 Thread tr1 = new Thread(new ThreadStart(() => { for (int i = 1; i <= 4; i++) { Console.WriteLine(hashtb[i]); } })); tr1.Start(); Thread tr2 = new Thread(new ThreadStart(() => { for (int i = 1; i <= 4; i++) { Console.WriteLine(hashtb[i]); } })); tr2.Start(); //修改 Thread tr3 = new Thread(new ThreadStart(() => { for (int i = 1; i <= 4; i++) { hashtb[i] = "ee"; } })); tr3.Start();

运行结果:正常

说明:Hashtable 是线程安全的,可由多个读取器线程和一个写入线程使用。多线程使用时,如果只有一个线程执行写入(更新)操作,则它是线程安全的,从而允许进行无锁定的读取(若编写器序列化为 Hashtable)

代码五、

Thread tr1 = new Thread(new ThreadStart(() => { lock (hashtb) { foreach (DictionaryEntry item in hashtb) { Console.WriteLine(item.Value); } } })); tr1.Start(); Thread tr2 = new Thread(new ThreadStart(() => { lock (hashtb) { for (int i = 1; i <= 4; i++) { hashtb[i] = "ee"; } } })); tr2.Start();

运行结果:正常

说明:由于两个线程里面都加了lock (hashtb)把hashtable锁住,所以是线程安全的。

代码六、

Thread tr1 = new Thread(new ThreadStart(() => { //锁住 lock (hashtb) { foreach (DictionaryEntry item in hashtb) { Console.WriteLine(item.Value); } } })); tr1.Start(); Thread tr2 = new Thread(new ThreadStart(() => { //未锁住 for (int i = 1; i <= 4; i++) { hashtb[i] = "ee"; } })); tr2.Start();

运行错误:集合已修改;可能无法执行枚举操作。

说明:从头到尾对一个集合进行枚举本质上并不是一个线程安全的过程。即使一个集合已进行同步,其他线程仍可以修改该集合,这将导致枚举数引发异常。若要在枚举过程中保证线程安全,可以在整个枚举过程中锁定集合,或者捕捉由于其他线程进行的更改而引发的异常。

Hashtable 提供的线程安全方法

Hashtable的Synchronized静态方法提供线程安全的实例,如下:

Hashtable ht = Hashtable.Synchronized(new Hashtable());

内部实现如下:

public override void Add(object key, object value) { lock (this._table.SyncRoot) {   this._table.Add(key, value); } }

按输入方式输出

因为hashtable内部是无序的,所以输出不一定,hashtable取数据的机制没搞明白。按照下面代码可以实现先进先出。

可以通过控制ArrayList里面keys的排序来控制hashtable的输出,当然也可以用SortedDictionary和SortedList实现排序集合。

public class NoSortHashtable : Hashtable { private ArrayList keys = new ArrayList(); public NoSortHashtable() { } public override void Add(object key, object value) { base.Add (key, value); keys.Add (key); } public override ICollection Keys { get { return keys; } } public override void Clear() { base.Clear (); keys.Clear (); } public override void Remove(object key) { base.Remove (key); keys.Remove (key); } public override IDictionaryEnumerator GetEnumerator() { return base.GetEnumerator (); } }

[转载]如何将ASP.NET MVC2项目升级到MVC 3 RC

mikel阅读(832)

[转载]如何将ASP.NET MVC2项目升级到MVC 3 RC – 紫色永恒 – 博客园.

微软在10号发布了ASP.NET MVC3的发布候选版(RC:Release-candidate),该版本提供了Razor视图引擎的智能感知等功能,并同样具备Online许可。眼馋的同学是不是已经迫不及待的想将现有项目升级呢?这里我就将官方ReleaseNotes中的升级办法翻译总结一下,以方便那些不喜欢看罗里啰嗦的英文文档的广大同学。

译文

要将现有的ASP.NET MVC2项目手动升级到ASP.NET MVC3(RC),我们需要如下这些步骤:

1. 随便创建一个新的ASP.NET MVC3项目,我们在升级中将用到其中包含的一些文件。

2. 从新建项目中将如下这些文件copy到现有的ASP.NET MVC2项目中,它们是:

· /Scripts/JQuery.unobtrusive-ajax.js

· /Scripts/JQuery.unobtrusive-ajax.min.js

· /Scripts/jQuery.validate.unobtrusive.js

· /Scripts/jQuery.validate.unobtrusive.min.js

· /Views/Web.config

3. 如果你现有的ASP.NET MVC2项目中包含若干area,则需要将/Views/Web.config文件copy到所有的area下替换原文件。

4 在项目的Web.config文件中(根目录下的Web.config中有三处,Views文件夹下的Web.config文件中有4处)分别搜索和替换如下内容:

System.Web.Mvc, Version=2.0.0.0 → System.Web.Mvc, Version=3.0.0.0

5. 在解决方案浏览器中删除System.Web.Mvc的引用(因为这里引用的是ASP.NET MVC2中大的DLL)然后添加一个System.Web.Mvc (v3.0.0.0)的引用。接下来再分别引用System.WebPages.dll和System.Web.Helpers.dll。

6. 在解决方案浏览器中鼠标右击项目名称,并选择卸载项目。接着再次右击项目名称然后选择编辑ProjectName.csproj。

7. 在ProjectName.csproj中找到ProjectTypeGuids节点,将 {F85E285D-A4E0-4152-9332-AB1D724D3325}替换为{E53F8FEA-EAE0-44A6-8774-FFD645390401}。

8. 保存第7步中的更改后右击项目,重新加载它。

9. 在应用程序根目录下的Web.config中的assemblies节点添加如下两条

image

10. 如果项目中引用了其他使用到ASP.NET MVC2中的dll协同编译的第三方类库,则需要做如下改动

image

大功告成,尽情享受ASP.NET MVC的魅力吧。

[转载]Java实现导入Excel

mikel阅读(1104)

[转载]Java实现导入Excel – JAVA之恋 – 博客园.

上班的时候公司要求做一个从网页上导入excel,研究了半天后,开始着手去实现它。

思路很简单:

1、做一个jsp页面,页面包括浏览文件,提交文件

2、将excel文件上传到服务器

3、  服务器对该excel文件进行读出

4、  将excel文件内容显示到页面上

环境搭建:

需要准备的包:commons-fileupload-1.2.1.jar & commons-io-1.3.2.jar 这两个包是上传用的

jxl.jar 这个包是读取excel用的 下载地址 :http://sourceforge.net/projects/jexcelapi/ 建议不要用新版本,因为新版本会出现与jdk版本兼容问题,如果运行程序出现问题的时候请切换旧版本。

一、Jsp页面

注意:1、在jsp页面的form要使用html本身的<form>标记,而不要使用第三方视图开源框架的form标记,例如不要使用strut的<htm:form>。

2、在<form>的属性里必须加上  ENCTYPE=”multipart/form-data”

1 <h1>导入Excel</h1> 2 <hr> 3 <form action="importExcel" method="post" enctype="multipart/form-data"> 4 <input type="file" name="importExcel" id="importExcel"> 5 <input type="submit" value="导入"> 6 </form>

二、上传excel的Servlet

注意:1、导入的excel最好用后缀为.xls,如果用.xlsx可能会导不进去。

2、在调用FileItem的write方法前必须保证文件的存放路径存在否则出现异常。commons fileupload不会自动为你建立不存在的目录。

3、上传后会对文件进行重命名,以时间为文件名进行命名

1 public class ImportExcelServlet extends HttpServlet { 2 //缓冲区域 3   File tempPathFile; 4 //默认路径 5   String uploadTo = "D:\\"; 6 // 支持的文件类型 7   String[] errorType = { ".xls" }; 8 //格式化日期 9   SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS"); 10 11 @Override 12 protected void doGet(HttpServletRequest req, HttpServletResponse resp) 13 throws ServletException, IOException { 14 req.setCharacterEncoding("utf-8"); 15 resp.setCharacterEncoding("utf-8"); 16  //取得服务器真实路径 17   uploadTo = req.getSession().getServletContext().getRealPath("\\") + "upload\\"; 18 // Create a factory for disk-based file items 19   DiskFileItemFactory factory = new DiskFileItemFactory(); 20 // 设置缓冲区大小,这里是4kb 21   factory.setSizeThreshold(4096); 22 // 设置缓冲区目录 23   factory.setRepository(tempPathFile); 24 // Create a new file upload handler 25   ServletFileUpload upload = new ServletFileUpload(factory); 26 // Set overall request size constraint 27 // 设置最大文件尺寸,这里是4MB 28 upload.setSizeMax(4*1024*1024); 29 // 开始读取上传信息 30 List fileItems = new ArrayList(); 31 try { 32 fileItems = upload.parseRequest(req); 33 } catch (FileUploadException e1) { 34 e1.printStackTrace(); 35 } 36 // 依次处理每个上传的文件 37 Iterator iter = fileItems.iterator(); 38 System.out.println("fileItems的大小是" + fileItems.size()); 39 // 正则匹配,过滤路径取文件名 40 String regExp = ".+\\\\(.+)$"; 41 Pattern p = Pattern.compile(regExp); 42 while (iter.hasNext()) { 43 FileItem item = (FileItem) iter.next(); 44 // 忽略其他不是文件域的所有表单信息 45 System.out.println("正在处理" + item.getFieldName()); 46 if (!item.isFormField()) { 47 String name = item.getName(); 48 long size = item.getSize(); 49 if ((name == null || name.equals("")) && size == 0) 50 continue; 51 Matcher m = p.matcher(name); 52 boolean result = m.find(); 53 if (result) { 54 boolean flag = false; 55 for (int temp = 0; temp < errorType.length; temp++) { 56 if(m.group(1).endsWith(errorType[temp])) { 57 flag = true; 58 } 59 } 60 if(!flag) { 61 System.out.println("上传了不支持的文件类型"); 62 throw new IOException(name + ": wrong type"); 63 } 64 try { 65 String fileName = uploadTo + format.format(new Date()) + m.group(1).substring(m.group(1).indexOf(".")); 66 item.write(new File(fileName)); 67 //调用ReadExcel类进行读出excel 68 ReadExcel.readExcel(fileName, resp.getWriter()); 69 System.out.println(name + "\t\t" + size); 70 } catch (Exception e) { 71 e.printStackTrace(); 72 } 73 } 74 } else { 75 // 这里添加对不是上传文件表单项的处理 76 System.out.println("这是一个表单项"); 77 } 78 } 79 80 } 81 82 @Override 83 protected void doPost(HttpServletRequest req, HttpServletResponse resp) 84 throws ServletException, IOException { 85 doGet(req, resp); 86 } 87 88 @Override 89 public void init() throws ServletException { 90 tempPathFile = new File("d:\\temp\\buffer\\"); 91 if (!tempPathFile.exists()) { 92 tempPathFile.mkdirs(); 93 } 94 } 95 } 96

三、读出excel文件内容的类

1 public class ReadExcel { 2 3 public static void readExcel(String pathname, PrintWriter out) { 4 try { 5 //打开文件 6 Workbook book = Workbook.getWorkbook(new File(pathname)) ; 7 //取得第一个sheet 8 Sheet sheet = book.getSheet(0); 9 //取得行数 10 int rows = sheet.getRows(); 11 for(int i = 0; i < rows; i++) { 12 Cell [] cell = sheet.getRow(i); 13 for(int j=0; j<cell.length; j++) { 14 //getCell(列,行) 15 out.print(sheet.getCell(j, i).getContents()); 16 out.print("&nbsp;"); 17 } 18 out.println("<br/>"); 19 } 20 //关闭文件 21 book.close(); 22 } catch (BiffException e) { 23 e.printStackTrace(); 24 } catch (IOException e) { 25 e.printStackTrace(); 26 } 27 } 28 29 } 30

总结:上面只是一个很简单的导入excel文件的例子,如果想做完善还得下更多的功夫。在做的过程中如果出现Workbook打不开,请更换jxl 版本,尽量用低版本,这样与jdk兼容会好点,我在做这个导入excel的时候,就遇到了版本兼容问题,处理了半天才发现问题所在。所以想做这个例子给大 家参考,以后不要犯和我同样的错误。O(∩_∩)O哈哈~

[转载]深入理解ASP.NET MVC(4)

mikel阅读(929)

[转载]深入理解ASP.NET MVC(4) – P_Chou Go deep and Keep learning – 博客园.

到目前为止Route对象只剩下DataTokens属性没有涉及,事实上这个Areas机制的核心。

DataTokens实际上也是一个RouteValueDictionary,在用MapRoute方法构造在Route构造的时候,可以传一个namespaces字符串数组,这个参数会构造成Route对象的DataTokens[“Namespaces”],它的值将被MVC框架优先用来在对应的名字空间中查找相应的Controller。 如果在指定的名字空间中能找到Controller,那么,就算在其他名字空间中有相同名字的Controller(大小写敏感)也没关系;如果在指定的 名字空间中没有找到Controller,那么将在所有引用的程序集中查找,此时如果出现重复名字的Controller,那么将出现多个匹配的错误。这 种行为是DefaultControllerFactory实现的,关于DefaultControllerFactory将在以后分析。

Areas机制是这样的一种机制:在不同的Area中可以有相同名字的Controller,也就是说Controller的名字可以重复了!这样 整个web应用程序可以按功能划分成几个模块,每个模块是一个Area,每个Area互相独立,可以独立地由某个开发人员随意定义URL或 Controller而不影响其他Area。

比如:有管理员和用户两个模块,也许需要如下的URL:

Admin/Home/Index

User/Home/Index

于是可以用VS集成的Area生成模板创建两个Area:Admin和User,分别地,由两个开发人员分别负责开发,他们都需要用 HomeController和Index方法,有了Areas机制,HomeController被分别放到两个不同的名字空间中,这就不会有冲突。

讲到这里你也许隐约明白DataTokens和Areas机制的某种关系了。在vs创建Areas的时候到底做了哪些事情呢?

一、首先在每个Area中Controller都将被放置到一个名字空间中,例如:MyAppName.Areas.Admin.Controllers;

二、为每个Area创建一个AreaRegistration的继承类,如果是Admin的Area将是AdminAreaRegistration。在这个类中重写AreaName属性和RegisterArea方法:

1 public override void RegisterArea(AreaRegistrationContext context)
2 {
3 context.MapRoute(
4 "Admin_default",
5 "Admin/{controller}/{action}/{id}",
6 new { action = "Index", id = UrlParameter.Optional }
7 );
8 }

可以看到在RegisterArea方法中也调用了MapRoute方法注册路由。需要注意的是这个的MapRoute虽然也是操作全局路由表,但是它的实现略有不同:

1.首先设置的URL Pattern是以Area名字开头的,这样做是必要的,毕竟这个URL Pattern最终是放在全局中的;

2.它会将DataTokens[“Namespaces”]设置成当前Area的名字空间,比如 MyAppName.Areas.Admin.*。结果是,当一个请求到来是,DefaultControllerFactory会优先到这个名字空间下 查找Controller。而且会增加一个DataTokens[“UseNamespaceFallback”],并设置为false,这样当且仅当显示设置的名字空间中有需要的Controller时,才能成功,其他名字空间的的同名Controller将无效;

3.最后,还会添加一个叫DataTokens[“area”]的键值,并设置为当前Area名字,这是为了在反向映射(outbounding)URL的时候使用。因此在MVC中”area”键是有特殊用途的,所以不能用于url pattern的参数。

在Areas机制中有一个冲突需要注意。由于路由表只有一张,如果当前的url映射到了”root area”(即在Global域),那么将从当前所有的名字空间中查找Controller,此时很可能找到多个匹配的。解决方案是,在 Globla.asax.cs中设置路由的时候,为DataTokens设置优先名字空间。

进一步扩展

当从深层次了解了路由工作机制后,就进行一些自定义了。

自定义RouteBase

有前面的分析,可以知道,在inbound时Route(继承自RouteBase)需要提供一个RouteData,因此RouteBase定义 了GetRouteData方法,这是我们可以自己实现的;同时,GetVirtualPath方法用于outbound。所以,只要实现了这两个方法就 可以完成一个RouteBase的实现。比如:当想要把一个老的网站改造成新的基于MVC架构的,又不想使原来的url失效,简单的处理方案可以像下面这 样:

01 public class LegacyUrlsRoute : RouteBase
02 {
03 // In practice, you might fetch these from a database
04 // and cache them in memory
05 private static string[] legacyUrls = new string[] {
06 "~/articles/may/zebra-danio-health-tips.html",
07 "~/articles/VelociraptorCalendar.pdf",
08 "~/guides/tim.smith/BuildYourOwnPC_final.asp"
09 };
10 public override RouteData GetRouteData(HttpContextBase httpContext)
11 {
12 string url = httpContext.Request.AppRelativeCurrentExecutionFilePath;
13 if(legacyUrls.Contains(url, StringComparer.OrdinalIgnoreCase)) {
14 RouteData rd = new RouteData(this, new MvcRouteHandler());
15 rd.Values.Add("controller", "LegacyContent");
16 rd.Values.Add("action", "HandleLegacyUrl");
17 rd.Values.Add("url", url);
18 return rd;
19 }
20 else
21 return null; // Not a legacy URL
22 }
23 public override VirtualPathData GetVirtualPath(RequestContext requestContext,
24 RouteValueDictionary values)
25 {
26 // This route entry never generates outbound URLs
27 return null;
28 }
29 }

自定义IRouteHandler

通常在MVC框架中IRouteHandler由MvcRouteHandler实现,这个MVC框架的入口。尽管如此,我们还是可以自己定义一个 IRouteHandler。当我们需要对某些请求做优化处理的时候可以考虑这样做。因为,自定义实现IRouteHandler意味着将忽略MVC框 架。比如下面这个实现:

public class HelloWorldHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new HelloWorldHttpHandler();
}
private class HelloWorldHttpHandler : IHttpHandler
{
public bool IsReusable { get { return false; } }
public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello, world!");
}
}
}