MVC Action with Optional Parameters -- which is better?
Solution 1
I have never seen the second action signature in practice and can't see any usefulness of it.
The first one usually covers all the scenarios:
- If no parameter is sent (
GET /somecontroller/action
), the value of the x argument will be null inside the action - If a x parameter is sent, but it is not a valid integer (
GET /somecontroller/action?x=abc
), the value of the x argument will be null inside the action and the modelstate will be invalid - If a x parameter is sent and the value represents a valid integer (
GET /somecontroller/action?x=123
), then x will be assigned to it.
In my examples I have used GET requests with query string parameters but obviously the same applies with other HTTP verbs and if x
was a route parameter.
Solution 2
You only need to specify the optional parameter value if it is going to be anything else other than null
.
MVC3 will automatically set null
as the value of your parameter if nothing is specified in the overload, or in the call to the Action.
However, its worth noting that if there are any non-optional parameters after this parameter in the signature, then null
would have to be specified in the call.
Therefore its best to put all optional params at the end of the signature.
Solution 3
Best Asp.net MVC solution - use action method selector
Why not simplify controller action methods by removing unnecessary code branch and have this kind of code as seen here:
public ActionResult Index()
{
// do something when there's no id
}
[RequiresRouteValues("id")]
public ActionResult Index(int id)
{
// do something when id is present
}
This is of course possible, as long as you provide the very simple code for RequiresRouteValuesAttribute
action method selector. You can find code in this blog post that does exactly this.
By my opinion this is the best possible solution to this problem, because:
- It simplifies code by removing unnecessary branch
- Makes code easier to maintain (due to lower complexity)
- Extends Asp.net MVC framework as it can and should
- Keeps parameter types as they should be without the need to make them nullable
- etc.
Anyway. All the details about this technique is explained in great detail in linked post.
Related videos on Youtube
Zaid Masud
Updated on July 09, 2022Comments
-
Zaid Masud almost 2 years
Are there any pros/cons of using the following two alternatives in your action signature:
public ActionResult Action(int? x) // get MVC to bind null when no parameter is provided { if(x.HasValue) { // do something } }
OR
public ActionResult Action(int? x = null) // C# optional parameter (virtual overload) { if(x.HasValue) { // do something } }
-
V4Vendetta about 12 yearsI think in the first case you would need the parameter while in the other case it would assume null if none are passed, So its actually the second one which you really want (just a guess).
-
Zaid Masud about 12 years@V4Vendetta no the first case does not need an explicit parameter when invoked as a web request.
-
-
Zaid Masud about 12 yearsPlease expand on what you mean when you say "is null anyway"? It's true that the MVC framework will bind it to a null value when no parameter is provided, but be aware that it's MVC doing this for you. In the second alternative we are explicitly specifying an overload.
-
RickAndMSFT about 12 yearsgood answer but note that int? x = null does nothing. null is the default value
-
Zaid Masud about 12 yearsIs this only applicable to the id parameter defined in the routing definition? If the parameter was called something else, I believe the RequiresRouteValues attribute would no longer be required and the solution would be similar to my second code sample, admittedly with less branching. Thank you for the idea but you might be answering a different question here.
-
Robert Koritnik about 12 yearsNo this is applicable to any controller action parameter. It doesn't mean that provided parameter names have to be in routing definition. They may be related to query values, post fields as well... And no, it wouldn't be similar to your second example at all. This attribute is an action method selector and not an action filter... If particular action parameter is not part of request (as part of route values, query variables or post fields) this method will not be executed at all. MVC wouldn't resolve this method as a possible candidate for request processing.
-
Robert Koritnik about 12 years@zooone9243: It is of course true, that my answer doesn't directly answer the question you've asked, but it provides a much better and cleaner solution that wouldn't get you to your question in the first place.
-
Doctor Jones almost 12 yearsHi @Darin, I've got a request with no parameter sent, but the value of my argument is not being set as null, instead it is the object's default value. I'd prefer a null though. Any ideas on what could be causing it? I'd open a new question but it'd probably get closed as a dupe!
-
Zaid Masud over 11 years@DoctaJonez assuming you are specifying a nullable type such as int? in the signature?
-
Zaid Masud over 11 yearsRobert I would suggest posting this as an answer to this question.
-
Doctor Jones over 11 years@ZaidMasud yes it's a nullable int. I figured out that it was picking up the value from elsewhere. I can't remember the exact details, but it was picking up the default value of a different field (not in the model) that happened to have the same name. Sorry I can't be more specific!
-
Robert Koritnik over 11 years@ZaidMasud: That's an old long overdue question that has been sufficiently answered and covers 90% of such cases. Action method selectors are advanced topic in MVC. Action renaming is not...
-
JoeBrockhaus almost 11 yearsThis was a great solution that I've been using for some time. However, I came across a case where it doesn't work as expected. If you want your action method to be chosen when empty/null param values are sent, ie:
?item1=&item2&=item3=value
then you need to modify theIsValidForRequest()
method ofRequiresRouteValuesAttribute
.QueryString.AllKeys
will return a singlenull
'key' foritem1=&item2=
. To solve this, add the following to theif(this.IncludeQueryVariables)
conditional:uniques.UnionWith(controllerContext.HttpContext.Request.QueryString.GetValues(null));
-
JoeBrockhaus almost 11 yearsjust to add to that previous comment, you need to make sure
QueryString.GetValues(null) != null
before usingUnionWith(..)
:: just to add to that previous comment, you need to make sure GetValues doesn't return null before Unioning with the uniques collection...if (controllerContext.HttpContext.Request.QueryString.GetValues(null) != null) uniques.UnionWith(controllerContext.HttpContext.Request.QueryString.GetValues(null));