使用razorgenerator在不同项目中使用相同名称的2个剃刀局部视图

时间:2011-10-11 11:19:00

标签: asp.net-mvc asp.net-mvc-3 razor

在我们的项目中,我们使用razorgenerator of David Ebbo。这允许我们将一些cshtml文件移动到类库中。

我们现在想要实现的目标如下:

  • MyCommonViews在其Views文件夹中有一个“MyView.cshtml”。
  • MyWebProject ALSO在其Views文件夹中有一个“MyView.cshtml”。
  • MyOtherWebProject在其Views文件夹中没有“MyView.cshtml”。

当MyOtherWebProject需要加载MyView.cshtml时,它将选择已编译的MyCommonViews项目中的那个。这就是我们想要的。
但是当MyWebProject需要加载MyView.cshtml时,我们希望它能够获取MyWebProject本身中的“重写”MyView.cshtml文件。

我们想要的是什么以及如何?

马努。

3 个答案:

答案 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);
}

这就是诀窍。我们基本上强制PrecompiledMvc​​Engine“忘记”我们在具体项目中的任何视图。

答案 1 :(得分:2)

您也可以尝试使用RazorGenerator.Mvc 2.1.0中的CompositePrecompiledMvc​​Engine。它旨在正确支持多个程序集中的视图覆盖。一段代码:

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