我正在尝试使用自定义(派生)RazorViewEngine 和使用 RazorGenerator 的预编译视图。
一些上下文:
我们有一个用于多个客户端实现的基础产品。有了这些,我们就有了一组核心的基本视图。大多数视图大部分时间都有效。现在,我们最终会为每个新解决方案复制现有视图并根据需要进行修改。最终,客户之间 95% 的视图是相同的,而 5% 的视图发生了变化。
我想要做的是获取一组基本视图,将它们编译成一个 DLL 并在客户端之间重新使用它。到目前为止,我使用 RazorGenerator 运行良好。
现在下一步是允许自定义(覆盖)视图。不过有一个警告。我们的应用程序有两种用户所处的“模式”。他们所处的模式可能需要不同的视图。
我从 RazorGeneratorView 创建了一个派生类。该视图主要检查 Autofac 解析的 UserProfile 对象中的“OrderingMode”。基于模式 - 路径定位器被替换为视图分辨率。
单个客户端应用程序的想法将首先尝试在传统的 Views 文件夹中解析视图。只有我在 Views/{OrderingMode}/{Controller}/{View}.cshtml 的子目录中添加。
如果未找到视图 - 那么它将在编译的库(核心视图)中查找。
这使我可以根据客户的需要覆盖单个视图/部分。
public PosViewEngine() : base()
{
//{0} = View Name
//{1} = ControllerName
//{2} = Area Name
AreaViewLocationFormats = new[]
{
//First look in the hosting application area folder / Views / ordering type
//Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml
"Areas/{2}/Views/%1/{1}/{0}.cshtml",
//Next look in the hosting application area folder / Views / ordering type / Shared
//Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml
"Areas/{2}/Views/%1/Shared/(0}.cshtml",
//Finally look in the IMS.POS.Web.Views.Core assembly
"Areas/{2}/Views/{1}/{0}.cshtml"
};
//Same format logic
AreaMasterLocationFormats = AreaViewLocationFormats;
AreaPartialViewLocationFormats = new[]
{
//First look in the hosting application area folder / Views / ordering type
//Areas/{AreaName}/{OrderType}/{ControllerName}/Partials/{PartialViewName}.cshtml
"Areas/{2}/Views/%1/{1}/Paritals/{0}.cshtml",
//Next look in the hosting application area folder / Views / ordering type / Shared
//Areas/{AreaName}/{OrderType}/{ControllerName}/{ViewName}.cshtml
"Areas/{2}/Views/%1/Shared/(0}.cshtml",
//Finally look in the IMS.POS.Web.Views.Core
"Areas/{2}/Views/{1}/{0}.cshtml"
};
ViewLocationFormats = new[]
{
"Views/%1/{1}/{0}.cshtml",
"Views/%1/Shared/{0}.cshtml",
"Views/{1}/{0}.cshtml",
"Views/Shared/{0}.cshtml"
};
MasterLocationFormats = ViewLocationFormats;
PartialViewLocationFormats = new[]
{
"Views/%1/{1}/Partials/{0}.cshtml",
"Views/%1/Shared/{0}.cshtml",
"Views/{1}/Partials/{0}.cshtml",
"Views/Shared/{0}.cshtml"
};
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
return base.CreatePartialView(controllerContext, partialPath.ReplaceOrderType(CurrentOrderingMode()));
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
OrderType orderType = CurrentOrderingMode();
return base.CreateView(controllerContext, viewPath.ReplaceOrderType(orderType), masterPath.ReplaceOrderType(orderType));
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
return base.FileExists(controllerContext, virtualPath.Replace("%1/",string.Empty));
}
private OrderType CurrentOrderingMode()
{
OrderType result;
_profileService = DependencyResolver.Current.GetService<IUserProfileService>();
if (_profileService == null || _profileService.OrderingType == 0)
{
IApplicationSettingService settingService =
DependencyResolver.Current.GetService<IApplicationSettingService>();
result =
settingService.GetApplicationSetting(ApplicationSettings.DefaultOrderingMode)
.ToEnumTypeOf<OrderType>();
}
else
{
result = _profileService.OrderingType;
}
return result;
}
}
这是 RazorGenerator 用于注册 ViewEngine 的 StartUp 类。
public static class RazorGeneratorMvcStart
{
public static void Start()
{
var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly)
{
UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal
};
ViewEngines.Engines.Insert(0, engine);
// StartPage lookups are done by WebPages.
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}
}
问题是:
- 此代码最后执行(在我注册 PosViewEngine 之后)并将引擎插入到第一个位置(这意味着这是在提供响应时第一个解析的引擎)。这最终找到了一个视图——它是核心视图。
如果我将 StartUp 中的代码更改为首先注册我的自定义视图引擎,然后是 RazorGenerator 引擎
public static void Start() { var engine = new PrecompiledMvcEngine(typeof(RazorGeneratorMvcStart).Assembly) { UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal }; ViewEngines.Engines.Clear(); ViewEngines.Engines.Insert(0, new PosViewEngine()); ViewEngines.Engines.Insert(1, engine); // StartPage lookups are done by WebPages. VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); }
我最终在 FileExists(ControllerContext controllerContext, string virtualPath) 方法上遇到了一个异常 - “此处不允许使用相对虚拟路径 'Views/Account/LogOn.cshtml'。”
它显然与物理和虚拟路径混合在一起有关。
看起来其他人试图在这里做同样的事情,但我没有看到答案。