Recently, I found myself copying views around in ASP.NET MVC application and changing nothing or just a few things, generally name of controller on BeginForm("actionName", "controllerName")
(and thankfully, this can be solved inside view). Shortly, my main purpose is to eliminate duplicate views.
Reason
The reason for this is that I have different controllers with similar functionality. They seem same to user but calculations are very different in controllers. More specifically I have four controllers called InitialReport, CorrectionReport, InitialReportReview, CorrectionReportReview. The views of InitialReport, CorrectionReport and InitialReportReview, CorrectionReportReview are almost identical. So what I want to do is to eliminate duplicate views and create shared views for couples.
Problem
My problem is that, I do not want to put all views to the Shared view folder, because they are too many to put to this folder. Additionally, names of actions in controllers overlaps. Say I have ReportPayments
action method in all four controllers, so name of views also overlaps. But the view must be different for InitialReport
and InitialReportReview
for example. So I have added two folders to Shared view folder called Report (for InitialReport, CorrectionReport) and ReportReview (for InitialReportReview, CorrectionReportReview). I plan to add all the shared views to Report and ReportReview sub-folders respectively. Now the problem is that, view look up locations must dynamically change to suit "controller type". For example, if I'm in InitialReport
controller and I am navigation to ReportPayments
action, then the view must be loaded from ~/Views/Shared/Report/ReportPayments.cshtml, but if I'm in InitialReportReview
controller, then the view should be loaded from ~/Views/Shared/ReportReview/ReportPayments.cshtml.
Solution
As a solution I have created custom view engine, I override FindView
method and set the ViewLocationFormats
according to controller name.
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);
}
}
I have registered ExtendedRazorViewEngine
as default view engine and it seems to work fine. But I have few questions. There is a question and answer which are similar (not identical) to mine, and quite helpful indeed, but it does not answer to my questions.
Questions
- Is this correct way? Does this method have any drawback such as views are not cached or any other performance issues? Can I make this method any better?
- Is there any better way of achieving my goal?
- When I tested, I realized that
FindView
method is hit twice. Why? I copied view engine and tested it in a almost empty MVC application, same thing happened there also, so I do not think it is related to my application.