0

Action’s parameters and Selection Decision

by volkanuzun 21. December 2009 22:38

    When you have an action, with an overloaded version of it in the controller, its parameters are not used to to decide the selection.

Let’s assume you have a form, and user clicks on the submit button, to send the form to the server. The action that renders the view for the get request is: Register(), and the action that receives the request is Register(FormCollection collection) similar to something like this:

public ViewResult Register()
{
    //do some prep for the view
    return View();
}

public ViewResult Register(FormCollection collection)
{
    //do something with the collection
   // either return a view, or redirect to another action
}

I didn’t put any action selector on purpose; to prove my point. If you run this page, and try to call /Register you can get an error. Basically the framework is complaining that it can’t pick which Register() action it should call to respond /Register. You may think that because the second Register() action has a parameter, it should not be picked for a get request; however it is not the case :)

Let’s look at the MVC source code to see how an action is executed. If you open the source code, and check the Controller.cs file, around line 162, you will this function:

 

   1:    protected override void ExecuteCore() {
   2:              TempData.Load(ControllerContext, TempDataProvider);
   3:   
   4:              try {
   5:                  string actionName = RouteData.
GetRequiredString("action");
   6:                  if (!ActionInvoker.InvokeAction
(ControllerContext, actionName)) {
   7:                      HandleUnknownAction(actionName);
   8:                  }
   9:              }
  10:              finally {
  11:                  TempData.Save(ControllerContext, TempDataProvider);
  12:              }
  13:          }

 

Look carefully at line 6. This where the action is invoked, now we have to dig in more, and see what is happening inside ActionInvoker.InvokeAction method. This method is doing 1 thing that is important for the topic of this blog:

 ActionDescriptor actionDescriptor = FindAction(controllerContext,
controllerDescriptor, actionName);
 
 

It is trying to find the action to execute, given the action name, and the controller name. It adds the results into a list of methodinfo. Here is it:

   public MethodInfo FindActionMethod(ControllerContext controllerContext, 
string actionName) { List<MethodInfo> methodsMatchingName = GetMatchingAliasedMethods
(controllerContext, actionName); methodsMatchingName.AddRange(NonAliasedMethods[actionName]); List<MethodInfo> finalMethods = RunSelectionFilters(
controllerContext, methodsMatchingName); switch (finalMethods.Count) { case 0: return null; case 1: return finalMethods[0]; default: throw CreateAmbiguousMatchException(finalMethods,
actionName); } }


After it adds the methods that it finds to the list, it runs the RunSelectionFilters function to eliminate the methods. After the eliminations, if it has more than 1 method left in the list, it throws the exception; which you see when you call /Register. As you can see the parameters of the action has nothing to do with the selection.
One way to help the framework to pick the right action is to use SelectionFilters; as you can see from the code above, FindActionMethod() calls the RunSelectionFilter() to eliminate the actions.

If you use AcceptVerbs selection filter to differentiate between the two Register function, framework won’t be complaining as before; so this is one way of doing it right:

[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Register()
{
    //do some prep for the view
    return View();
}

[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Register(FormCollection collection)
{
    //do something with the collection
   // either return a view, or redirect to another action
}
 

This time, when there is a get request, RunSelectionFilter() will eliminate Register(FormCollection collection), as it has the AcceptVerbs post selecttionfilter.

I am still walking through the ASP.NET MVC source code, and really enjoying the way the developers wrote it; it is really fun to read it.

Have fun :)

Tags:

MVC

0

MVC Case Sensitivity & Action Names & NonAction Attribute

by volkanuzun 6. December 2009 02:44

Today, it was the first class of our free ASP.NET MVC course. I was talking about URLs not being case sensitive. So that for example if you have the request “Home/About” this goes to HomeController and About action, as well as hOmE/AbOUT is going to the same controller and same action method.
One of the students ask me, what will happen if we have 2 about action methods in the same controller with different cases such as:

public class HomeController:Controller
{
....
    public ViewResult About()
    {
        return View();
    }

    public ViewResult aBOut()
    {
        return View();
    }
}

If you write the code above, and try to run the app, and call Home/About, here is the error message you will get:

image

The framework can’t determine which ‘about’ function to call, and throws the exception telling that the call is ambiguous. Of course one way to fix this problem is to change the action name. If for some reason you don’t want to change the action name, and one of these function is not an action, then you can decorate this non action method with NonAction attribute. Example:

[NonAction]
public ActionResult aBOut()
{
   return View();
}

So now this function is not anymore an action method that will handle the web requests. What is NonAction attribute?
It is an attribute that derives from ActionMethodSelectorAttribute. When you derive from this attribute you can override IsValidForRequest function, and return false, to refuse to serve as an action. If you check the MVC source code, here is the code for NonAction attribute:

[AttributeUsage(AttributeTargets.Method, 
AllowMultiple = false, Inherited = true)] public sealed class NonActionAttribute : ActionMethodSelectorAttribute { public override bool IsValidForRequest(ControllerContext
controllerContext, MethodInfo methodInfo) { return false; } }
 

This code is refusing to act as a request. So you can use this NonAction attribute on any of your public functions inside a controller to make them not web accessible.

Tags:

MVC

2

Why Controllers should have a default constructor?

by volkanuzun 13. November 2009 03:21

    I am still in the journey of exploring MVC source code.  Yesterday, I wrote about controllers and why they need “Controller” suffix. Today, I will try to find out in the code, why does a controller need a default constructor. First of all, what is default constructor?
You can check wikipedia for its complete definition; but basically a default constructor is a constructor that has no parameters. If there is no other constructors in the code, compiler puts a default constructor for you in the code. You don’t believe me? Here is a simple class:

public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }


This class does not have any type of constructors, so the compiler put a default constructor for us. compile the project, check the assembly with either reflector or ildasm tool. You will see the default constructor in your compiled code (besides some other stuff):

image 
.ctor() is the default constructor that the compiler added for us, when we did not supply any constructor. So what happens when we add a constructor that takes a parameter but no default constructor:

public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public Person(string FirstName)
        {
            this.FirstName = FirstName;
        }
    }

We now have a constructor that takes the FirstName as a parameter, and we don’t have a default constructor. This time if you check the assembly again, you will find:

image

You see that, the constructor .ctor does take 1 parameter, and there is no default constructor for us. This means when you want to create a new instance of Person, you can NOT use parameter less constructor such as: Person p = new Person();  You have to call it with a parameter.
So far, this is basic C#, where is MVC section? Here you are:

When a request comes into the MVC, we saw that the framework finds the controller first. Inside the DefaultControllerFactory (if you downloaded the source code, it is in DefaultControllerFactory.cs line # 83), an instance of the controller is being created to process the request. Here is the code from the controller factory:

 protected internal virtual IController GetControllerInstance
(Type controllerType) { // some code is here... try { return (IController)Activator.CreateInstance(controllerType); } // some other code is here...

Activator.CreateInstance is used to create an instance of the controller. Let’s see MSDN for Activator.CreateInstance:

“CreateInstance(Type)    Creates an instance of the specified type using that type's default constructor.” So Activator.CreateInstance(controllerType) will create an instance of the specified controller using the controller’s default constructor. This is the main reason controllers should have a constructor, as DefaultControllerFactory creates the controller by using its default constructor.  Luckily if we don’t have a default constructor, the framework will throw an exception at runtime when it is trying to generate a new instance (I wish we could have compile time error).  The error you will have is very self explanatory.

image 

The framework is even showing us the exact location that throws the exception. So we need to supply a default constructor if we have defined constructors with parameters.
Why would you have a constructor in your controller that takes parameter if the framework does not call it? For 2 simple reasons:

  1. Unit Testing: You want to unit test your controller, and by supplying some values in the constructor you can swap the dependencies with some other implementations that you have control of.
  2. You want to do inversion of control. Basically if you have a class, and your class depends on some other entities (such as your controller needs a class to communicate with database, or a logger class to log the errors), it shouldn’t be a task of your controller to create an instance of this classes that it depends. The classes  should be instantiated and given to your controller ready to use.

 

Let’s see an example again, assume you have a DatabaseLogger class which derives from ILogger interface and sends the logged message to the database:

public interface ILogger
{
    void SendMessage(string Message);
}

public class DatabaseLogger: ILogger
{
    public void SendMessage(string Message)
   {
      // some code to write the message to the database


Our HomeController needs the DatabaseLogger class to send some messages. One way  of doing this is to put the instantiation inside the default controller such as:

public class HomeController:Controller
{
    private ILogger logger;

    public HomeController():base(new DatabaseLogger())
    {     
    }

    public HomeController(ILogger logger)
    {
        this.logger = logger;
    }
}

There are 2 constructors in this controller. The second one that has a parameter, sets the controller’s dependency to the passed in logger interface. The default constructor is creating an instance of DatabaseLogger and passing this to the second constructor. With this technique here we can unit test our controller as the second constructor gives us the ability to swap the ILogger with an object that we can control.  However, if you look at the default constructor, the controller is creating the class that it has the dependency on. It should be passed the depedency ready to use. In this simple example maybe the benefit is not obvious however think that DatabaseLogger() needs some construction parameters that will setup the database connection string, and maybe to read the database connection string, you have some other dependencies such as a configuration file reader. Now your homecontroller has to supply all these settings to create the DatabaseLogger. This is too much now, this is where you need to use dependency injection such as Castle Windsor, StructureMap, Ninject etc…

This will be another story :)

Tags: ,

MVC

1

Why Controllers should have a “Controller” Suffix?

by volkanuzun 12. November 2009 07:06

   If you are programming with ASP.NET MVC, you know that the controller’s should have a suffix of “Controller” such as “HomeController”. You may also know that this is how DefaultControllerFactory works.
Let’s look into the technical details. I will skip some unrelated facts, but we will check the MVC source code, and modify it a little. Please go to CodePlex and download the MVC source code.
When a request comes into IIS (Microsoft’s web server application), IIS first checks the request, then the MVCRouteHandler is called (assuming this is an MVC request). The MVCRouteHandler creates an instance of  MvcHandler by passing the request context. You can check out the code in the MVCRouteHandler.cs file.

protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
            return new MvcHandler(requestContext);
        }


After creating the instance, ProcessRequest (from MVCHandler) is invoked and HttpContext is passed as the parameter. The ProcessRequest function is below:

 protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
            AddVersionHeader(httpContext);

            // Get the controller type
            string controllerName = RequestContext.RouteData.
GetRequiredString("controller"); // Instantiate the controller and call Execute IControllerFactory factory = ControllerBuilder.
GetControllerFactory(); IController controller = factory.CreateController(RequestContext,
controllerName);
.... some other calls


As you can see, this function calls the GetControllerFactory() method, to get the ControllerFactory. If you haven’t registered a new ControllerFactory, this function returns the DefaultControllerFactory. The next call in ProcessRequest function is CreateController.  So let’s see what is happening in the DefaultControllerFactory’s CreateController function.

public virtual IController CreateController(RequestContext requestContext, 
string controllerName) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (String.IsNullOrEmpty(controllerName)) { throw new ArgumentException(
MvcResources.Common_NullOrEmpty, "controllerName"); } RequestContext = requestContext; Type controllerType = GetControllerType(controllerName); IController controller = GetControllerInstance(controllerType); return controller; }


This function after checking requestContext, and controllerName against null values, calls GetControllerType. Here is what GetControllerType does:

protected internal virtual Type GetControllerType(string controllerName) {
            if (String.IsNullOrEmpty(controllerName)) {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty,
"controllerName"); } // first search in the current route's namespace collection object routeNamespacesObj; Type match; match = GetControllerTypeWithinNamespaces
(controllerName, nsHash); if (match != null) { return match; } } }


This is not the whole function but a piece of it.

Again after checking some values against null, it is calling the main function which is GetControllerTypeWithinNamespaces.  I am sure you are already bored from one function call to another function call :) however, I will be writing more about the MVC infrastructure, so it is good to show them. So one more time, let’s look at the GetControllerTypeWithinNamespace function.

The first thing this function does is to call: ControllerTypeCache.EnsureInitialized(BuildManager). This will load all the controllers and cache them, so that the next time a controller is needed, the framework doesn’t have to find and load the controllers. This is a simple yet pretty important function.

 public void EnsureInitialized(IBuildManager buildManager) {
     if (_cache == null) {
          lock (_lockObj) {
            if (_cache == null) {
              List<Type> controllerTypes = GetAllControllerTypes(buildManager);
              var groupedByName = controllerTypes.GroupBy(           
      t => t.Name.Substring(0, t.Name.Length - "Controller".Length),
                            StringComparer.OrdinalIgnoreCase);
       _cache = groupedByName.ToDictionary(
                            g => g.Key,
                            g => g.ToLookup(t => t.Namespace ?? 
String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } }


As you can see this function caches the controllers after loading them. The _cache variable that is used in this function is a Dictionary<string,ILookup<string,type>>. So basically the framework loads the controllers into a cache. The function that loads the controllers is GetAllControllerTypes. It loads all the assemblies, iterating through them by calling a function: IsControllerType. This is why, this needs to be cached, as you don’t want to load all the assemblies and check if they are controllers. So this IsControllerType function is the reason why we have to add suffix “Controller” to our controllers:

 internal static bool IsControllerType(Type t) {
            return
                t != null &&
                t.IsPublic &&
                t.Name.EndsWith("Controller", 
                StringComparison.OrdinalIgnoreCase) &&
                !t.IsAbstract &&
                typeof(IController).IsAssignableFrom(t);
        }


This function is checking if the passed in controller, has a suffix of “Controller” or not. If it does, it returns true, and GetAllControllerTypes adds this controller to the list, and returns the list of controllers.

The call goes back to EnsureInitialized which strips out the Controller suffix, and adds it into the cache. Let’s assume we want our controllers to have suffix of “Volkan” ; what do we need to do?

First you have to change the IsControllerType so that it checks .EndsWith(“Volkan”) not “Controller”. This will let the <controllerName>Volkan controllers to be added to the list. But this is not enough, the next thing we have to do is, go back to EnsureInitialized and change the stripping part from t.Name.Length - "Controller".Length to t.Name.Length - "Volkan".Length .

Finally you have to put the “Volkan” suffix to your controllers, and now this becomes a valid controller:

      public class HomeVolkan : Controller{

 

Enjoy your time debugging the MVC code!

Tags: ,

MVC

Powered by BlogEngine.NET 1.6.0.0
Original Design by Laptop Geek, Adapted by onesoft