wind

a blog of erpcrm

 

Oxite移植到ASP.NET MVC2 BETA 笔记(关于Html.RenderPartialFromSkin)

      在将Oxite移植到asp.net mvc2 beta平台后,经过一系列有关“方法调用”变更的修正后,终于能够通过编译运行起来了!(移植后的源码参见:http://ecubecms.codeplex.com/)但经过测试,发现存在一个问题:

      在site.master中的<%Html.RenderPartialFromSkin("HeadCustomContents"); %>无法加载到"~/skins/admin/Views/…”下的*.ascx了("~/Views/”下的可以),换句话说Oxite中的Htmlhelper扩展方法RenderPartialFromSkin方法无法加载指定皮肤的视图了。下面对该问题的根源和如何解决做一分析。

一、Oxite 皮肤机制的简单分析。

1、Oxite首先实现了自己的ViewEngine,即Oxite.Skinning.OxiteWebFormViewEngine。

OxiteWebFormViewEngine继承自System.Web.MVC.WebFormViewEngine并实现了IOxiteViewEngine接口。它的主要职责是通过设置RootPath来封装请求的文件路径。

2、Oxite自动注册了一个实现IResultFilter接口的类Oxite.Filters.ViewEnginesResultFilter。ViewEnginesResultFilter的OnResultExecuting方法中根据指定的皮肤来实例化几个OxiteWebFormViewEngine(一个皮肤实例化一个,由SkinResolverRegistry.GenerateViewEngines方法来完成的。)并存储到ResultExecutingContext.Result.ViewEngineCollection和ViewData["OxiteViewEngines"]。

检索皮肤的优先顺序:

      request.QueryString["skin"]

      request.Cookies["skin"]

      request.Url.PathAndQuery.StartsWith("/Admin")

      ViewData["Skin"]  

      Site.Skin

3、Controller.View和PartialView方法通过ResultExecutingContext.Result.ViewEngineCollection检索到View(.aspx,.ascx)。而Html.RednerPartial方法则是直接通过ViewEngines.Engines集合来检索View,结果是检索不到有皮肤的View的。所以Oxite定制了RenderPartialFromSkin的Htmlhelper扩展方法,它不通过ViewEngines.Engines而通过ViewData["OxiteViewEngines"](别忘了上面提到过它是由ViewEnginesResultFilter.OnResultExecuting方法存进去的)以检索到View。

二、为什么加载不到有皮肤的ascx(aspx)文件了?

      通过上面的分析,RenderPartialFromSkin方法加载View的原理很清楚了,在ASP.NET MVC1.0中运行的也非常好。但为什么移植到ASP.NET MVC2 BETA就不行了呢?

先看一下Oxite中的RenderPartialFromSkin方法加载到ViewEngines后都干了些什么?

foreach (IViewEngine viewEngine in (IEnumerable<IOxiteViewEngine>)newViewData[viewEngineCollectionName])

{
          ViewEngineResult result = viewEngine.FindPartialView(htmlHelper.ViewContext, partialViewName, true);

}

上面这段代码的意思是,从newViewData[viewEngineCollectionName](注:ViewData["OxiteViewEngines”])中检索每个IViewEngine,并调用它们的FindPartialView方法。也就是:此处调用的FindPartialView方法在asp.net mvc2 beta下并没有找到由该方法的参数partialViewName指定的View

三、如何初步解决问题。

      先来看一下IViewEngine.FindPartialView方法的定义:

ViewEngineResult IViewEngine.FindPartialView(ControllerContext controllerContext,string partialViewName,bool useCache)

注意一下第三个参数:useCache。RenderPartialFromSkin方法调用时传的是true值(意思是使用缓存),那不使用缓存是不是能找到View呢,就把ture改为false。即这样调用:ViewEngineResult result = viewEngine.FindPartialView(htmlHelper.ViewContext, partialViewName,false);// true);

竟然成功了!至此,问题已初步解决。但“不使用缓存”,心里总有些遗憾。

四、深入分析问题的根源并完全解决问题。

      如何解决上面提到的“遗憾”呢?首先的思路是在OxiteWebFormViewEngine中重载FindPartialView方法,这样我不但要写大量代码,而且要考虑缓存等各种情况,会深陷细节的泥沼。那只有换另一个思路了,能不能不直接调用FindPartialView方法。通过反射htmlhelper的RenderPartial方法,发现它最终是通过调用ViewEngineCollection.FindPartialView(ControllerContext controllerContext,string partialViewName),这个方法没有useCache参数,终于见到了解决问题的一丝曙光。

马上修改Oxite的有关RenderPartialFromSkin方法源码。

1、Oxite的ViewEnginesResultFilter中放入ViewData["OxiteViewEngines"]中的是IEnumerable<IOxiteViewEngine>,我把它改为ViewEngineCollection放进去。

IEnumerable<IOxiteViewEngine> viewEngines = skinResolvers.GenerateViewEngines(new SkinResolverContext(requestContext, skin), skin);
ViewEngineCollection vec
= new ViewEngineCollection(viewEngines.Cast<IViewEngine>().ToList());
viewData[
"OxiteViewEngines"] = vec;

注:此处修改的是Oxite.Filters.ViewEnginesResultFilter.setupSkinViewEngines()方法,位于Oxite(项目)/Filters/ViewEnginesResultFilter.cs中。

2、在renderPartialFromSkin方法中改成这样调用:

ViewContext vc = new ViewContext(htmlHelper.ViewContext, htmlHelper.ViewContext.View, newViewData,htmlHelper.ViewContext.TempData);
ViewEngineCollection vec
= newViewData[viewEngineCollectionName] as ViewEngineCollection;
ViewEngineResult result
= vec.FindPartialView(vc, partialViewName);

注:此处修改的是Oxite.Extensions.HtmlHelperExtensions.renderPartialFromSkin()方法,位于Oxite(项目)/Extensions/HtmlHelperExtensions.cs中。

至此,问题终于较完美的解决。

源码可参考我的ECubeCMS项目:http://ecubecms.codeplex.com/

posted on 2009-11-26 10:57  WIND  阅读(1930)  评论(4编辑  收藏  举报

导航

ECubeCMS