【发布时间】:2016-09-30 13:08:38
【问题描述】:
最近,我发现自己在 ASP.NET MVC 应用程序中复制视图并没有更改任何内容或仅更改一些内容,通常是 BeginForm("actionName", "controllerName") 上的控制器名称(谢天谢地,这可以在视图内部解决)。很快,我的主要目的是消除重复视图。
原因
原因是我有不同的控制器具有相似的功能。它们对用户来说似乎相同,但控制器中的计算非常不同。更具体地说,我有四个控制器,分别称为 InitialReport、CorrectionReport、InitialReportReview、CorrectionReportReview。 InitialReport、CorrectionReport和InitialReportReview、CorrectionReportReview的视图几乎相同。所以我想做的是消除重复视图并为夫妻创建共享视图。
问题
我的问题是,我不想将所有视图放到 Shared 视图文件夹中,因为它们太多了,无法放入此文件夹。此外,控制器中的动作名称重叠。假设我在所有四个控制器中都有ReportPayments 操作方法,因此视图名称也重叠。但是例如 InitialReport 和 InitialReportReview 的视图必须不同。所以我在 Shared 视图文件夹中添加了两个文件夹,名为 Report(用于 InitialReport、CorrectionReport)和 ReportReview(用于InitialReportReview, CorrectionReportReview)。我计划将所有共享视图分别添加到 Report 和 ReportReview 子文件夹中。现在的问题是,视图查找位置必须动态更改以适应“控制器类型”。例如,如果我在 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 注册为默认视图引擎,它似乎工作正常。但我有几个问题。有一个 question 和 answer 与我的相似(不完全相同),确实很有帮助,但它不能回答我的问题。
问题
- 这是正确的方法吗?此方法是否有任何缺点,例如 视图未缓存 或任何其他 性能问题?我可以改进这个方法吗?
- 有没有更好的方法来实现我的目标?
- 当我测试时,我意识到
FindView方法被命中了两次。为什么?我复制了视图引擎并在一个几乎空的 MVC 应用程序中对其进行了测试,同样的事情也发生在那里,所以我认为它与我的应用程序无关。
【问题讨论】:
-
这太过分了。 Jesse Sierks 是对的——不要让它变得更难。使用内置选项提供视图名称,并将差异提取到动态加载的局部视图中。您可以将这些名称存储在 - 例如 - ViewBag 集合中。
-
@komsky,感谢您的意见,但这不是我想要的。这不是矫枉过正,我想编写一段功能良好的代码并摆脱重复,在每个动作中指定完整的视图路径,这是我不想在我的控制器代码中看到的东西。
标签: c# asp.net asp.net-mvc razorengine