[ASP.NET MVC 专题] ViewEngine的发展以及应用
[ASP.NET MVC 专题]
[ASP.NET MVC 专题] 如何为Route构造相关的自定义Configuration
[ASP.NET MVC 专题] ViewEngine的发展以及应用
ViewEngine的发展以及应用
(一)MVC1版本的ViewEngine
从上图,我们可以知道:(1)当客户端发送请求时,DefaultControllerFactory根据RequestContext对象和ControllerName来生成我们的Controller。Controller的ViewDataDictionary将保存相关的数据,并且通过ViewEngine传递给View。
public virtual IController CreateController(RequestContext requestContext, string controllerName)
{
Type controllerType = this.GetControllerType(requestContext, controllerName); //type = this.GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, namespaces);
return this.GetControllerInstance(requestContext, controllerType);
}
protected internal virtual IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return (IController) Activator.CreateInstance(controllerType);//反射
}
(2)在我们以前的MVC版本中,Controller拥有ViewEngine的属性。我们先看下下述的代码:ViewEngine依据ViewContext,ViewLocator怎么处理View以及响应客户端??
public void RenderView(ViewContext viewContext)
{
string viewLocation = ViewLocator.GetViewLocation(viewContext, viewContext.ViewName);//根据ViewContext以及相关内容获取view的相对文件路径
string viewPath = viewContext.HttpContext.Request.MapPath(viewLocation););//获取view的绝对文件路径
string viewTemplate = File.ReadAllText(viewPath);//读取文件内容
IRenderer renderer = new PrintRenderer();
viewTemplate = renderer.Render(viewTemplate, viewContext);//正则表达式处理相应内容,难点!
viewContext.HttpContext.Response.Write(viewTemplate);//发送响应到客户端
}
从上面的代码中,我们能够清晰的看到,总体框架就是这么赤裸裸的“请求--->响应”。虽然代码是过去版本的,但是依旧能够使我们比较容易的理解整个过程的细节。
关于以前版本的自定义ViewEngine请参考http://blog.maartenballiauw.be/post/2008/05/Creating-a-custom-ViewEngine-for-the-ASPNET-MVC-framework.aspx
(二)随着MVC版本的改进,我们的ViewEngine一直在变,从未被超越。ViewLocator不再使用了,将属性整合到ViewEngine中了。先看下我们的继承链上的LocationFormats,如下:
{
public string[] AreaMasterLocationFormats { get; set; }
public string[] AreaPartialViewLocationFormats { get; set; }
public string[] AreaViewLocationFormats { get; set; }
public string[] MasterLocationFormats { get; set; }
public string[] PartialViewLocationFormats { get; set; }
public string[] ViewLocationFormats { get; set; }
}
上面的6个属性就涵盖了我们的代码文件的相对路径形式。如果要改变我们的代码文件路径,我们需要设置一下我们自己的LocationFormats。我们仅仅需要做的就是将父类的属性重新设置成相关的路径形式既可,如下示例:
2 {
3 public HomeViewEngine(): this("Default")
4 {
5 }
6 public HomeViewEngine(string directory){
7 MasterLocationFormats = new[]{
8 string.Format("~/{0}/{{1}}/{{0}}.master", directory),
9 string.Format("~/{0}/shared/{{0}}.master", directory)
10 };
11 ViewLocationFormats = new[]{
12 string.Format("~/{0}/{{1}}/{{0}}.aspx", directory),
13 string.Format("~/{0}/{{1}}/{{0}}.ascx", directory),
14 string.Format("~/{0}/shared/{{0}}.aspx", directory),
15 string.Format("~/{0}/shared/{{0}}.ascx", directory)
16 };
17 PartialViewLocationFormats = ViewLocationFormats;
18 }
19 public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache){
20 return base.FindView(controllerContext, viewName, masterName, useCache);
21 }
22 public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache){
23 return base.FindPartialView(controllerContext, partialViewName, useCache);
24 }
25 }
将属性重新赋值后,相对路径就改变了,系统会通过GetLocation()方法获取每次请求匹配的相对视图路径地址。我们可以通过ViewEngineCollection(IList<T>)来添加自己的ViewEngine。如下所示,我们通过双检锁的技巧(盗版的)来添加新的ViewEngine。其他的留给系统去考虑吧,XX,封装的太厉害了,哥也有时候摸不清方向!
public class BaseHomeController :Controller
{
public static object lockObj = new object();
private HomeViewEngine viewEngine = null;
public BaseHomeController(){
if (viewEngine == null){
lock (lockObj){
if (viewEngine == null){
viewEngine = new HomeViewEngine();//设置你的路径前缀目录名称,可为空
ViewEngines.Engines.Add(viewEngine);
}
}
}
}
}
将你的相关的代码文件放在你想放置的地方,对于public HomeViewEngine(string directory){}构造函数,我们实例化 HomeViewEngine时传递相关的参数既可。系统会根据相关路径查找文件。
(三)RazorViewEngine的相对文件路径如下:
{
base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
base.AreaPartialViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.cshtml", "~/Areas/{2}/Views/{1}/{0}.vbhtml", "~/Areas/{2}/Views/Shared/{0}.cshtml", "~/Areas/{2}/Views/Shared/{0}.vbhtml" };
base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
base.PartialViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml", "~/Views/{1}/{0}.vbhtml", "~/Views/Shared/{0}.cshtml", "~/Views/Shared/{0}.vbhtml" };
this.ViewStartFileExtensions = new string[] { "cshtml", "vbhtml" };
}
我们同样可以通过继承来覆盖原有的LocationFormats,这样,我们的视图文件路径也就可以随心所欲的放置在自己的目录下了。
更多Razor应用的请参考http://weblogs.asp.net/rashid/archive/2010/07/10/use-razor-as-asp-net-mvc-viewengine.aspx
总结
本文稍微的总结了ViewEngine的发展以及如何在项目中应用。随着MVC版本的改进,封装越来越多,很多细节被掩盖了。在Razor中,系统根据ViewEngine的相关路径属性获取的文件内容,系统如何处理@指令的?
Regex templatePattern = new Regex(@"@???", RegexOptions.Multiline);//@??
MatchEvaluator replaceCallback = new MatchEvaluator(m => HandleRegexData());
templatePattern.Replace(viewTemplate, replaceCallback);这个需进一步学习才行。