自定义VirtualPathProvider映射ASP.NET MVC View
假设有多个ASP.NET MVC 应用解决方案,基于Area部署同一个站点。多个站点需要以一致的方式进行布局切换,不同Area映射View的方式不同,它们都以{AreaName}\{Views}的格式存放。相比这种访问方式,将多个Area关联的View更新到一个固定目录更便于管理,比如:App_Themes\{SkinName}\Views\{AreaName}\。自定义ViewEngine可以关联起来,还有一种方式就是自定义VirtualPathProvider。比如当请求一个/Views/Home/Index.cshtml,ASP.NET会通过VirtualPathProvider的FileExists询问路径是否存在,如果存在则会继续调用GetFile获取一个VirtualFile。我们可以将一个View的内容存放到数据库或者添加一个映射配置文件。比如对于A应用的Index.cshtml的虚拟路径:/A/Views/Home/Index.cshtml实际映射到/App_Themes/{SkinName}/Views/A/Index.cshtml,只需要继承于VirtualFile并重写Open函数。下面是一个简单示例:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <map> 3 <view virtualPath="" mapTo="" /> 4 </map>
1 /// <summary> 2 /// 视图映射 3 /// </summary> 4 [XmlType(TypeName = "view")] 5 public class ViewMap 6 { 7 /// <summary> 8 /// 虚拟路径 9 /// </summary> 10 [XmlAttribute(AttributeName = "virtualPath")] 11 public String VirtualPath { get; set; } 12 13 /// <summary> 14 /// 映射到 15 /// </summary> 16 [XmlAttribute(AttributeName = "mapTo")] 17 public String MapTo { get; set; } 18 }
1 /// <summary> 2 /// 视图映射路径提供器 3 /// </summary> 4 public sealed class ViewMapPathProvider : VirtualPathProvider 5 { 6 /// <summary> 7 /// 序列化提供器 8 /// </summary> 9 private static readonly XmlSerializer SerializeProvider; 10 11 /// <summary> 12 /// 静态构造函数 13 /// </summary> 14 static ViewMapPathProvider() 15 { 16 SerializeProvider = new XmlSerializer(typeof(List<ViewMap>), new XmlRootAttribute("map")); 17 } 18 19 private IDictionary<String, ViewMap> m_views; 20 private FileSystemWatcher m_fileWatcher; 21 22 /// <summary> 23 /// 构造函数 24 /// </summary> 25 /// <param name="xmlPath">文件路径</param> 26 public ViewMapPathProvider(String xmlPath) 27 { 28 if (String.IsNullOrEmpty(xmlPath)) 29 throw new ArgumentException("xmlPath不能为空"); 30 31 if (!File.Exists(xmlPath)) 32 throw new FileNotFoundException(String.Format("文件{0}不存在", xmlPath)); 33 34 this.XmlPath = xmlPath; 35 36 m_fileWatcher = new FileSystemWatcher(Path.GetDirectoryName(xmlPath)); 37 m_fileWatcher.Changed += new FileSystemEventHandler(OnFileChanged); 38 } 39 40 /// <summary> 41 /// 文件路径 42 /// </summary> 43 public String XmlPath { get; private set; } 44 45 /// <summary> 46 /// 视图集合 47 /// </summary> 48 public IDictionary<String, ViewMap> Views 49 { 50 get 51 { 52 if (m_views == null) 53 { 54 using (TextReader textReader = new StreamReader(this.XmlPath, true)) 55 { 56 List<ViewMap> views = SerializeProvider.Deserialize(textReader) as List<ViewMap>; 57 58 m_views = views.ToDictionary(p => p.VirtualPath); 59 } 60 } 61 62 return m_views; 63 } 64 } 65 66 /// <summary> 67 /// 文件是否存在 68 /// </summary> 69 /// <param name="virtualPath">虚拟路径</param> 70 /// <returns>是否存在</returns> 71 public override Boolean FileExists(String virtualPath) 72 { 73 if (this.Views.ContainsKey(virtualPath)) 74 return true; 75 76 return base.FileExists(virtualPath); 77 } 78 79 /// <summary> 80 /// 获取文件 81 /// </summary> 82 /// <param name="virtualPath">虚拟路径</param> 83 /// <returns>虚拟文件</returns> 84 public override VirtualFile GetFile(String virtualPath) 85 { 86 if (this.Views.ContainsKey(virtualPath)) 87 { 88 ViewMap viewMap = this.Views[virtualPath]; 89 90 return new ViewMapFile(virtualPath, viewMap); 91 } 92 93 return base.GetFile(virtualPath); 94 } 95 96 /// <summary> 97 /// 文件变化 98 /// </summary> 99 /// <param name="sender">发送方</param> 100 /// <param name="e">事件参数</param> 101 private void OnFileChanged(Object sender, FileSystemEventArgs e) 102 { 103 if (String.Equals(e.FullPath, this.XmlPath, StringComparison.CurrentCultureIgnoreCase)) 104 m_views = null; 105 } 106 }
1 /// <summary> 2 /// 视图映射文件 3 /// </summary> 4 public class ViewMapFile : VirtualFile 5 { 6 /// <summary> 7 /// 构造函数 8 /// </summary> 9 /// <param name="virtualPath">虚拟路径</param> 10 /// <param name="viewMap">视图映射</param> 11 public ViewMapFile(String virtualPath, ViewMap viewMap) 12 : base(virtualPath) 13 { 14 this.ViewMap = viewMap; 15 } 16 17 /// <summary> 18 /// 视图映射 19 /// </summary> 20 public ViewMap ViewMap { get; private set; } 21 22 /// <summary> 23 /// 打开 24 /// </summary> 25 /// <returns>流</returns> 26 public override Stream Open() 27 { 28 return new FileStream(this.ViewMap.MapTo, FileMode.Open, FileAccess.Read); 29 } 30 }
1 /// <summary> 2 /// 启动 3 /// </summary> 4 protected void Application_Start() 5 { 6 AreaRegistration.RegisterAllAreas(); 7 8 RegisterGlobalFilters(GlobalFilters.Filters); 9 RegisterRoutes(RouteTable.Routes); 10 11 String xmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"App_Data\ViewMap.xml"); 12 HostingEnvironment.RegisterVirtualPathProvider(new ViewMapPathProvider(xmlPath)); 13 }