How to access controller methods from a view in ASP.NET MVC
This question has been posed in several places. One of the places is on StackOverflow here. The use case here is that my controller has important state or an important function, and I’d like to leverage it from the view. It’s not appropriate to factor out to an html helper or other view-based utility class. It is something unique to the controller. It would be very easy to just pass the controller over to the view so that the view could make use of its methods, but there is a lot of discussion why that is “dirty.”
For the lay-programmer, that solution would function just fine. For the professional, the MVC pattern was chosen to keep the view away from the business of knowing the storyboard flow of an application. That is the responsibility of the controller; therefore, the pattern states that the controller knows the view, but the view does not know the controller.
In ASP.NET MVC, the controller passes an object called a ViewBag to the view. As an aside, it is really interesting that when ASP.NET MVC 1 was being developed, many .Net developers were using MonoRail, which was another MVC implementation for ASP.NET. It’s passed object from the controller to the view was called PropertyBag, and many lobbied for the use of the word “bag”, but ViewData was the first term used. The new ViewBag object comes to us in ASP.NET MVC 3 as a dynamic object.
Desired use case
Let’s consider a page that lists out a series of odd numbers. We want to alert the user when there is an odd number that is not prime. Here is the screen.
The view for this screen looks like this.
Notice that inside my loop, I am actually passing the enumerated value of the view model to a method on the ViewBag. Remember that ViewBag doesn’t come with any methods like this. It is an empty dynamic object until we define something on it.
It appears that we have defined a new method on ViewBag called GetWarning(). This new method is the gateway that we longed for to take us back to the controller. Let’s look at the code for the controller, and we will see that the controller provides the logic for deciding what warning message to display next to the number, if any.
There are several things to look at. First, we define a property, not a method on the ViewBag. It is a Func, so it can be invoked because it is a method being passed as an object thanks to delegates/closures. The definition of the method that is passed takes in a number and makes use of a method that belongs to the controller. Because closures/delegates have access to the scope in which they are defined, our GetContextAwareWarning method can access the IsPrime method even when the method is invoked way over in the view.
Now, we can give the view everything it needs to function without ever being tempted to pass a reference to the controller itself.