Curious about MVC Controllers? - c#

Just a quick question, when you create a new controller for a new MVC ASP.Net app how does it know which controller to use. More specifically, given you create a new controller and you call it SockController in order to use said controller I would navigate to http://mywebapp/sock. How did the web app know that /Sock/ is linked to SockController? Is there a mapping somewhere ? Or if not what happens when if you call omit controller from the name when creating it, ie call it SockCont.
Note: I am not a Web Dev im just curious so please don't post links to page with tons of text, im looking for a short simple answer.

ASP.NET Routing extracts the name of the controller from the URL by getting the Route Value and then appending "Controller" to the end. So "/home/" returns "HomeController".
ASP.NET then uses reflection to go through every class in the project's assembly (or referenced assemblies) to find a class that inherits from System.Web.Mvc.Controller and is called "HomeController". It then uses the default, parameterless constructor to create an instance of it.
It then matches the Route action to a method of the controller.
This process is called "Dispatch" and similar patterns are seen in PHP, Ruby-on-Rails, etc, except that dynamic languages like those have different ways of resolving class names to actual objects (CakePHP uses Class auto-loading bindings to locate the class definition, for example).

Related

Surface Controller or Custom Controller in Umbraco 7?

History
I'm a Web Forms developer with some .NET MVC experience, new to Umbraco and learning as I go.
So far I've been following the Umbraco documentation and videos to get set up which means all my controllers inherit from a "Controller Base" with common functions included, which in itself inherits from SurfaceController.
Recently however, I have noticed some bloggers and external reference material referencing RenderMvcController in the base class instead of SurfaceController - now the Umbraco documentation is unclear on the real differences between the two, nor which situations you should use them in.
The Question
Is there a clear and distinct difference between the imagined usage scenarios for a Surface Controller - inheriting from Umbraco.Web.Mvc.SurfaceController, and a Custom Controller - inheriting from Umbraco.Web.Mvc.RenderMvcController?
Thanks!
The documentation for the SurfaceController is here: http://our.umbraco.org/documentation/Reference/Templating/Mvc/surface-controllers
In a nutshell, the SurfaceController is used for helping to interact with views. So for example you could post a form to a surface controller. Or you could write a child action to a view with a SurfaceController
RenderMvcController is used purely for routing to published pages. So you could sub-class RenderMvcController in order to 'hijack' requests to published pages of a specific Document Type. See here http://our.umbraco.org/documentation/Reference/Templating/Mvc/custom-controllers.
To further clarify based on Digbyswift's answer:
SurfaceController = APIs or form targets (that then redirect)
RenderMvcController = custom logic to build a model or select a view for a content item (based on Document Type and, optionally, template)

Split controllers into 2 separate DLLs

I have an idea I'd like to implement which basically involves separating the controllers in an MVC4 Project to 2 different projects. The reason being I'd like to be able to have different controllers for an internal admin section of my site and for the external client section. I'd like to make changes to each of them individually and add a new DLL to the website as I make changes...the changes to the internal admin section of the site thus not affecting the controllers DLL for the external client section for example.
Does anyone know if this is possible/advisable or of a better way to accomplish what I'm trying to achieve?
taken from an artical :
"The MVC framework provides a default controller factory (aptly named DefaultControllerFactory) that will search through all the assemblies in an appdomain looking for all types that implement IController and whose name ends with "Controller." Thus, if you tell the factory to look for a "Home" controller, the factory can return a newly instantiated instance of a HomeController class regardless of the namespace or assembly it lives in—as long as it implements IController...."
by adding a reference to another project in you'r solution you can achieve what you are looking for, in that referenced project add you'r controllers. like written above the MVC Routing will find all controllers that been referenced in that solution.

ASP.NET Routing: predict which route will be executed

I have a RouteCollection and a string with an URL.
Has anybody an idea how can I get the name of the route that would be executed?
Some method like RouteCollection.PredictRoute(url) that will return the route name or the physical file that would be executed.
I want to create some statistics from web server log files. It would be very nice to reuse the used RouteCollection for that statistic.
Many thanks!
Here is a snippet of code I used to test that URLs were being mapped to their correct controller and action. You should be able to inspect the route name as well.
RouteConfig.RegisterRoutes(routes);
RouteData routeData = routes.GetRouteData(context);
string controller = routeData.Values["controller"];
string action = routeData.Values["action"];
The variable routes is a RouteCollection object and context is a HttpContextBase object.
I assume you want to do this outside of your MVC application, where requests and contexts are not available (only the log files, as you mentioned).
Unfortunately, that is not very easy to do. In order to write such a matching method you need to reuse (or worse, rewrite) the MVC framework code for URL parsing found in various classes in namespace System.Web.Http.Routing. The source code is available on CodePlex, under Apache 2.0 license.
Instead, I think there's an easier way you can include the route names in the log files and use them later in statistics. Have a look below.
What you could to do is extend the default MvcHandler class and override its ProcessRequest method. In there, you can call the base class implementation and right after it do your stuff by using RequestContext.RouteData.
The class RouteData contains, besides other things, also the current route (which you defined in your route collection. That is accessible through the RouteData.Route property.
This works because MvcHandler is always delegated (unless you've created your own MVC infrastructure) by the MvcRouteHandler, to handle the processing for each request. It is the one that gets the controller data from the existing route data, instantiates an IController implementation, and ultimately executes it.

Change the Location of the Controller inside the Project structure

I found this Post (How to extend where MVC looks for views) about changing the location of the View.
I was wondering if there's something similar for changing the location of the controller.
I just want to change the location of the class inside project and don't want to affect the url.
For example Instead of placing the Controller into
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
I want to achieve something like
MyMvcProject\MyGroup1\
MyController1.cs
MyController2.cs
MyMvcProject\MyGroup2\
MyController3.cs
and also support Areas:
MyMvcProject\Areas\MyGroup3\
MyController4.cs
Is it possible to achieve this? And if yes, where can I find documentation about it?
You can do what you want, and it doesn't require any special configuration, because ASP.NET MVC does not care about where you put your controllers. First, controllers are located using reflection, so the name of the folder where you put your controllers is irrelevant. Controllers are searched by type name and optionally by namespace (for disambiguation). You can even have controllers in separate projects/assemblies. As long as the controller ends up in an assembly in the bin folder then it's searchable by the framework.
As mentioned above, you'll need to create a controller factory to support your custom resolution. Here's an example:
http://develoq.net/2010/custom-controller-factory-in-asp-net-mvc/
As others have already stated you need to do one of the following:
Derive from IControllerFactory interface and provide an implementation of the CreateController and ReleaseController methods.
Derive from DefaultControllerFactory and override the default behaviours.
Here are some links to get you started:
Custom controller factory in ASP.Net
Inside the ASP.NET MVC Controller factory
Dive deep into MVC - IControllerFactory
Also, if you're willing to spend a bit of money I would also recommend the book Pro ASP.NET MVC 3 Framework as this explains almost every aspect of how the MVC framework can be customised (including an example on how to create a custom controller factory - the source code for which can be freely downloaded from the publishers website).
I think it is impossible to do this. ASP.NET MVC have defined the convention that we have to follow.
Controllers are in Controllers folder, views are in Views{ControllerName}\
I believe you cannot change the convention unless you create your own ControllerFactory.
If you really want to do that, just implement IControllerFactory interface (or try to derive from DefaultControllerFactory).
Then your Application_Start register your controller factory using ControllerBuilder.Current.SetControllerFactory method.
Look at the ControllerFactory documentation and to the MVC source code for details.
What you're asking and what your example shows are two different things; depending on which one you want to achieve, you may or may not need to do any work.
There are two requirements for a class to be a controller in the MVC Framework:
It has to have a class name of Name + "Controller"
It has to have a parameterless public constructor.
Your sample "normal" MVC layout is actually not valid:
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
Those classes wouldn't be found by MVC because they don't have the correct name, regardless of which folder they are in.
If all you want to do is change the namespace/folder names, that "just works", assuming you name them the same as the appropriate route segment(s):
MyMvcProject\MyGroup1\
Page1Controller.cs
Page2Controller.cs
MyMvcProject\MyGroup2\
Page3Controller.cs
MyMvcProject\Areas\Area1\
Area1Page1Controller.cs
This walkthrough (written for MVC 2 but works just as well in MVC3) shows you how to support Areas with the default controller behavior.
If you actually want to name them SomethingController1 or SomethingElseController5, or otherwise change the route -> classname mappings, then you do need to implement a custom ControllerFactory, and inject it into the MVC pipeline.
There are plenty of examples on the web on how to do this, including the one posted earlier.

How are asp.net mvc 2 controllers instantiated?

When an asp.net application is notified of a URL, it routes it to the appropriate controller and specifically to the appropriate method.
Are those controllers placed upon the stack once? Or do they get instantiated again for each request?
For example, say I have a controller with a linq-to-sql class that gets instantiated in the declaration of the class. If I have n requests that route into that controller, have I spawned n different linq-to-sql class objects, each in their own instance of controller or just 1?
My gut tells me controllers are spawned one per request for thread safety reasons but I can't seem to dig up a better guide than my own gastrointestinal oracle.
They get instantiated each time by DefaultControllerFactory by default. Specifically, in GetControllerInstance,
(IController)Activator.CreateInstance(controllerType);
CreateController is first called which calls GetControllerType to get the controller type based on the controller name and the Namespaces passed in the route data tokens. Then it calls GetControllerInstance which creates an instance of the controller.
There's no better guide than the MVC framework source code itself.
You can define your own ControllerFactory by implementing IControllerFactory and then control how and when controllers are instantiated.

Categories

Resources