【问题标题】:Dynamic view search locations according to controller根据控制器动态视图搜索位置
【发布时间】:2016-09-30 13:08:38
【问题描述】:

最近,我发现自己在 ASP.NET MVC 应用程序中复制视图并没有更改任何内容或仅更改一些内容,通常是 BeginForm("actionName", "controllerName") 上的控制器名称(谢天谢地,这可以在视图内部解决)。很快,我的主要目的是消除重复视图。

原因

原因是我有不同的控制器具有相似的功能。它们对用户来说似乎相同,但控制器中的计算非常不同。更具体地说,我有四个控制器,分别称为 InitialReport、CorrectionReport、InitialReportReview、CorrectionReportReviewInitialReportCorrectionReportInitialReportReview、CorrectionReportReview的视图几乎相同。所以我想做的是消除重复视图并为夫妻创建共享视图。

问题

我的问题是,我不想将所有视图放到 Shared 视图文件夹中,因为它们太多了,无法放入此文件夹。此外,控制器中的动作名称重叠。假设我在所有四个控制器中都有ReportPayments 操作方法,因此视图名称也重叠。但是例如 InitialReportInitialReportReview 的视图必须不同。所以我在 Shared 视图文件夹中添加了两个文件夹,名为 Report(用于 InitialReport、CorrectionReport)和 ReportReview(用于InitialReportReview, CorrectionReportReview)。我计划将所有共享视图分别添加到 ReportReportReview 子文件夹中。现在的问题是,视图查找位置必须动态更改以适应“控制器类型”。例如,如果我在 InitialReport 控制器中并且我正在导航到 ReportPayments 操作,那么必须从 ~/Views/Shared/Report/ReportPayments.cshtml 加载视图,但是如果我在InitialReportReview 控制器中,那么应该从~/Views/Shared/ReportReview/ReportPayments.cshtml 加载视图。

解决方案

作为我创建自定义视图引擎的解决方案,我覆盖FindView 方法并根据控制器名称设置ViewLocationFormats

public class ExtendedRazorViewEngine
    : RazorViewEngine
{
    string[] DefaultViewLocations { get; set; }
    string[] ReportViewLocations { get; set; }
    string[] ReportReviewViewLocations { get; set; }

    public ExtendedRazorViewEngine()
    {
        // Get the copy of default view locations
        DefaultViewLocations = new string[ViewLocationFormats.Length];
        ViewLocationFormats.CopyTo(DefaultViewLocations, 0);

        // Initialize ReportViewLocations
        List<string> customReportViewLocations = new List<string>
        {
            "~/Views/Shared/Report/{0}.cshtml"
        };
        ReportViewLocations = DefaultViewLocations
            .Union(customReportViewLocations)
            .ToArray();

        // Initialize ReportReviewViewLocations
        List<string> customReportReviewViewLocations = new List<string>
        {
            "~/Views/Shared/ReportReview/{0}.cshtml"
        };
        ReportReviewViewLocations = DefaultViewLocations
            .Union(customReportReviewViewLocations)
            .ToArray();
    }

    public override ViewEngineResult FindView(
        ControllerContext controllerContext, 
        string viewName, 
        string masterName, 
        bool useCache)
    {
        // Get controller name
        string controllerName = controllerContext.RouteData.GetRequiredString("controller");

        // Set view search locations
        if (controllerName.EndsWith("ReportReview"))
            ViewLocationFormats = ReportReviewViewLocations;
        else if (controllerName.EndsWith("Report"))
            ViewLocationFormats = ReportViewLocations;
        else
            ViewLocationFormats = DefaultViewLocations;

        return base.FindView(controllerContext, viewName, masterName, useCache);
    }
}

我已将ExtendedRazorViewEngine 注册为默认视图引擎,它似乎工作正常。但我有几个问题。有一个 questionanswer 与我的相似(不完全相同),确实很有帮助,但它不能回答我的问题。

问题

  1. 这是正确的方法吗?此方法是否有任何缺点,例如 视图未缓存 或任何其他 性能问题?我可以改进这个方法吗?
  2. 有没有更好的方法来实现我的目标?
  3. 当我测试时,我意识到FindView 方法被命中了两次。为什么?我复制了视图引擎并在一个几乎空的 MVC 应用程序中对其进行了测试,同样的事情也发生在那里,所以我认为它与我的应用程序无关。

【问题讨论】:

  • 这太过分了。 Jesse Sierks 是对的——不要让它变得更难。使用内置选项提供视图名称,并将差异提取到动态加载的局部视图中。您可以将这些名称存储在 - 例如 - ViewBag 集合中。
  • @komsky,感谢您的意见,但这不是我想要的。这不是矫枉过正,我想编写一段功能良好的代码并摆脱重复,在每个动作中指定完整的视图路径,这是我不想在我的控制器代码中看到的东西。

标签: c# asp.net asp.net-mvc razorengine


【解决方案1】:

我认为你让这件事变得比需要的更难。将视图放在共享文件夹中似乎要简单得多。但如果您不想这样做,您仍然可以调用 View,即使它不在 Shared 文件夹中。

从一个角度来看:

@Html.Partial("~/Views/ReportReview/InitialReport.cshtml")

或来自控制器:

return this.View("~/Views/ReportReview/InitialReport.cshtml");

所以如果我是你,我会创建这些视图一次,然后从其他视图或控制器中调用那个视图。这样您就不必重复代码,也不必创建自定义视图引擎(我想这将是多年来维护的负担)。

【讨论】:

  • 感谢您的回答。正如我在问题中解释的那样,即使我想要,我也不能将所有视图放入共享文件夹,因为视图名称重叠,我需要两个不同的视图,例如 ReportPayments。而且我不想在我的控制器中指定确切的视图名称。如果你想象我有几十个动作,那么你可以说这是一个harderuglier的解决方案。
  • 视图名称重叠是我认为共享文件夹对您来说更容易/更好的原因。只需将模型传递给包含差异(操作、控制器等)的视图,或者使用 ViewBag 代替模型。只要 HTML 相同或基本相同。基本上,这正是共享视图的用途——防止重复的视图代码。或者我不明白这个问题。
  • 不,恐怕您的问题没有正确回答。现在我有四个视图,称为ReportPayments。两个用于 InitialReport、ConcreteReport(报告视图)和两个用于 InitialReportReview、ConcreteReportReview(报告审查视图)。 报告查看次数报告查看次数相同。现在,我需要两个视图,一个用于 InitialReport, ConcreteReport,另一个用于 InitialReportReview, ConcreteReportReview
  • 那么你有四个Views叫做ReportPayments,每个Views在不同的Controller下,而这些Views的写法是一样的吗?相同的 HTML 和代码? (除非类名或表单/链接操作等细微差别。)
  • 我错过了什么吗?并非所有四个都相同。让我试着解释一下:InitalReport == ConcreteReport &amp;&amp; InitialReportReview == ConcreteReportReview &amp;&amp; InitalReport != InitialReportReview &amp;&amp; ConcreteReport != ConcreteReportReview 将导致true
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-03
  • 2014-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多