在我们的项目中,我们使用razorgenerator of David Ebbo。这允许我们将一些cshtml文件移动到类库中。
我们现在想要实现的目标如下:
当MyOtherWebProject需要加载MyView.cshtml时,它将选择已编译的MyCommonViews项目中的那个。这就是我们想要的。
但是当MyWebProject需要加载MyView.cshtml时,我们希望它能够获取MyWebProject本身中的“重写”MyView.cshtml文件。
我们想要的是什么以及如何?
马努。
答案 0 :(得分:2)
我为我们的问题编写了一个hacky解决方案。它侵入了razorgenerators视图引擎并从它所拥有的(私有只读)字典中删除所有适当的条目。
代码在应用程序启动时运行。
谈话很便宜,请告诉我代码:
private static void HackRazorGeneratorToAllowViewOverriding()
{
// first we search for the PrecompiledMvcEngine
var razorGeneratorViewEngine = ViewEngines.Engines.ToList().FirstOrDefault(ve => ve.GetType().Name.Contains("PrecompiledMvcEngine"));
if (razorGeneratorViewEngine == null)
return;
// retrieve the dictionary where it keeps the mapping between a view path and the (view object) type to instantiate
var razorMappings = (IDictionary<string, Type>)ReflectionUtils.GetPrivateReadonly("_mappings", razorGeneratorViewEngine);
// retrieve a list of all our cshtml files in our 'concrete' web project
var files = Directory.GetFiles(Path.Combine(WebConfigSettings.RootPath, "Views"), "*.cshtml", SearchOption.AllDirectories);
// do some kungfu on those file paths so that they are in the same format as in the razor mapping dictionary
var concreteViewPaths = files.Select(fp => string.Format("~{0}", fp.Replace(WebConfigSettings.RootPath, "").Replace(@"\", "/"))).ToList();
// loop through each of the cshtml paths (of our 'concrete' project) and remove it from the razor mappings if it's there
concreteViewPaths.ForEach(vp =>
{
if (razorMappings.ContainsKey(vp))
razorMappings.Remove(vp);
});
}
WebConfigSettings.RootPath包含HD到我们Web应用程序根目录的路径。
这是我们的静态ReflectionUtils类的一部分:
/// <summary>
/// Get a field that is 'private readonly'.
/// </summary>
public static object GetPrivateReadonly(string readonlyPropName, object instance)
{
var field = instance.GetType().GetField(readonlyPropName, BindingFlags.Instance | BindingFlags.NonPublic);
if (field == null)
throw new NullReferenceException(string.Format("private readonly field '{0}' not found in '{1}'", readonlyPropName, instance));
return field.GetValue(instance);
}
这就是诀窍。我们基本上强制PrecompiledMvcEngine“忘记”我们在具体项目中的任何视图。
答案 1 :(得分:2)
您也可以尝试使用RazorGenerator.Mvc 2.1.0中的CompositePrecompiledMvcEngine。它旨在正确支持多个程序集中的视图覆盖。一段代码:
var engine = new CompositePrecompiledMvcEngine(
/*1*/ PrecompiledViewAssembly.OfType<MyCommonViewsSomeClass>(),
/*2*/ PrecompiledViewAssembly.OfType<MyWebProjectSomeClass>(
usePhysicalViewsIfNewer: HttpContext.Current.IsDebuggingEnabled));
ViewEngines.Engines.Insert(0, engine);
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
第一行将注册MyCommonViews程序集中的所有视图(〜/ Views / MyView.cshtml),第二行将注册MyWebProject或MyOtherWebProject程序集中的所有视图。
当遇到已经注册的虚拟路径(来自MyWebProject程序集的〜/ Views / MyView.cshtml)时,它会覆盖具有新视图类型映射的旧映射。
如果另一个项目没有具有相同虚拟路径的视图(MyOtherWebProject),则会保持源映射不变。
答案 2 :(得分:1)
标志PreemptPhysicalFiles = false
可以实现神奇。
完整样本:
[assembly: WebActivator.PostApplicationStartMethod(typeof(Application.Web.Common.App_Start.RazorGeneratorMvcStart), "Start")]
namespace Application.Web.Common.App_Start
{
public static class RazorGeneratorMvcStart
{
public static void Start()
{
var engine = new PrecompiledMvcEngine2(typeof (RazorGeneratorMvcStart).Assembly)
{
UsePhysicalViewsIfNewer = true, //compile if file changed
PreemptPhysicalFiles = false //use local file if exist
};
ViewEngines.Engines.Add(engine);//Insert(0,engine) ignores local partial views
// StartPage lookups are done by WebPages.
VirtualPathFactoryManager.RegisterVirtualPathFactory(engine);
}
}
}
然而,可能存在一个小错误: http://razorgenerator.codeplex.com/workitem/100