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
}
}
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.
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.
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
Under ASP.NET MVC are you supposed to pick up QueryString params the same way you do in ASP.NET WebForms? or does the [AcceptVerbs(HttpVerbs.Get)] declaration get used somehow?
Query string parameters can be accepted simply by using an argument on the action - i.e.
public ActionResult Foo(string someValue, int someOtherValue) {...}
which will accept a query like .../someroute?someValue=abc&someOtherValue=123
Other than that, you can look at the request directly for more control.
I think what you are looking for is
Request.QueryString["QueryStringName"]
and you can access it on views by adding #
now look at my example,,, I generated a Url with QueryString
var listURL = '#Url.RouteUrl(new { controller = "Sector", action = "List" , name = Request.QueryString["name"]})';
the listURL value is /Sector/List?name=value'
and when queryString is empty
listURL value is /Sector/List
You can always use Request.QueryString collection like Web forms, but you can also make MVC handle them and pass them as parameters. This is the suggested way as it's easier and it will validate input data type automatically.
I recommend using the ValueProvider property of the controller, much in the way that UpdateModel/TryUpdateModel do to extract the route, query, and form parameters required. This will keep your method signatures from potentially growing very large and being subject to frequent change. It also makes it a little easier to test since you can supply a ValueProvider to the controller during unit tests.
Actually you can capture Query strings in MVC in two ways.....
public ActionResult CrazyMVC(string knownQuerystring)
{
// This is the known query string captured by the Controller Action Method parameter above
string myKnownQuerystring = knownQuerystring;
// This is what I call the mysterious "unknown" query string
// It is not known because the Controller isn't capturing it
string myUnknownQuerystring = Request.QueryString["unknownQuerystring"];
return Content(myKnownQuerystring + " - " + myUnknownQuerystring);
}
This would capture both query strings...for example:
/CrazyMVC?knownQuerystring=123&unknownQuerystring=456
Output: 123 - 456
Don't ask me why they designed it that way. Would make more sense if they threw out the whole Controller action system for individual query strings and just returned a captured dynamic list of all strings/encoded file objects for the URL by url-form-encoding so you can easily access them all in one call. Maybe someone here can demonstrate that if its possible?
Makes no sense to me how Controllers capture query strings, but it does mean you have more flexibility to capture query strings than they teach you out of the box. So pick your poison....both work fine.
This is the correct way in .NET 6 (and other netcore)
var Param = Request.Query["IndexString"];
If need to be string
string Param = Request.Query["IndexString"].ToString();
#Context.Request.Query["yourId"]
Under ASP.NET MVC are you supposed to pick up QueryString params the same way you do in ASP.NET WebForms? or does the [AcceptVerbs(HttpVerbs.Get)] declaration get used somehow?
Query string parameters can be accepted simply by using an argument on the action - i.e.
public ActionResult Foo(string someValue, int someOtherValue) {...}
which will accept a query like .../someroute?someValue=abc&someOtherValue=123
Other than that, you can look at the request directly for more control.
I think what you are looking for is
Request.QueryString["QueryStringName"]
and you can access it on views by adding #
now look at my example,,, I generated a Url with QueryString
var listURL = '#Url.RouteUrl(new { controller = "Sector", action = "List" , name = Request.QueryString["name"]})';
the listURL value is /Sector/List?name=value'
and when queryString is empty
listURL value is /Sector/List
You can always use Request.QueryString collection like Web forms, but you can also make MVC handle them and pass them as parameters. This is the suggested way as it's easier and it will validate input data type automatically.
I recommend using the ValueProvider property of the controller, much in the way that UpdateModel/TryUpdateModel do to extract the route, query, and form parameters required. This will keep your method signatures from potentially growing very large and being subject to frequent change. It also makes it a little easier to test since you can supply a ValueProvider to the controller during unit tests.
Actually you can capture Query strings in MVC in two ways.....
public ActionResult CrazyMVC(string knownQuerystring)
{
// This is the known query string captured by the Controller Action Method parameter above
string myKnownQuerystring = knownQuerystring;
// This is what I call the mysterious "unknown" query string
// It is not known because the Controller isn't capturing it
string myUnknownQuerystring = Request.QueryString["unknownQuerystring"];
return Content(myKnownQuerystring + " - " + myUnknownQuerystring);
}
This would capture both query strings...for example:
/CrazyMVC?knownQuerystring=123&unknownQuerystring=456
Output: 123 - 456
Don't ask me why they designed it that way. Would make more sense if they threw out the whole Controller action system for individual query strings and just returned a captured dynamic list of all strings/encoded file objects for the URL by url-form-encoding so you can easily access them all in one call. Maybe someone here can demonstrate that if its possible?
Makes no sense to me how Controllers capture query strings, but it does mean you have more flexibility to capture query strings than they teach you out of the box. So pick your poison....both work fine.
This is the correct way in .NET 6 (and other netcore)
var Param = Request.Query["IndexString"];
If need to be string
string Param = Request.Query["IndexString"].ToString();
#Context.Request.Query["yourId"]
I'm working on a MVC web application. I need to download a file which I've stored as a byte[] stream in DB and its working fine. What I used to do on a button click I call a JS function and that calls a function in the C# backend and eventually download the file. Following is my JQuery code.
var DownloadDRR = function ()
{
var InvoiceId = $(".invoiceid").text();
location.href = location.origin + "/Invoicing/DownloadDRR?InvoiceId=" + InvoiceId;
}
And in the backend I normally get query string like this
Request.Querystring("InvoiceId");
But accidental I've discovered in my application if I write the following it still gets the InvoiceId without using Request.QueryString().
public FileResult DownloadDRR(int InvoiceId)
{
InvoicingService Invoices = new InvoicingService(Client);
byte[] RawExcel = Invoices.GetExcelService(InvoiceId);
MemoryStream stream = new MemoryStream(RawExcel);
stream.Seek(0, SeekOrigin.Begin);
return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "test.xlsx");
}
Can anyone explain why please?
MVC specifically automates a lot of that binding (model binding is the term used).
myurl.com/MyController/MyMethod?something=a&anotherthing=1234
//example 1
public ActionResult MyMethod(string something, string anotherthing)
//example2
public ActionResult MyMethod(string something, int anotherthing)
It works for both examples. Even though your querystring technically only contains string values, MVC will try to parse it to the desired type.
The only thing you need to pay attention to is that the querystring parameter names match the method's parameter names. The rest is done automagically :)
//example3
public ActionResult MyMethod(int something, int anotherthing)
In this example, something cannot be converted, as "a" cannot be put into an int. The method call will fail (expect an ASP.Net error page). However, there are ways around this:
If the type is nullable, the method will still be called, and null will be the value. int? something will be set as null if conversion fails, and this makes sure the method still gets called.
You can make it an optional parameter: MyMethod(int anotherthing, int something = 0). Notice the inversion of the parameters. Optional parameters must always be placed after normal (required) parameters! This will make sure that, when something either cannot be converted (or simply isn't part of the querystring), it will receive the default value you specified (in my example, 0)
Some remarks:
You can write custom modelbinders that go way deeper than just converting a value. However, this is not default MVC behavior. It's still good to know you can add it if you need it.
Not all parameters are always part of the querystring. If you make a POST request (as opposed to the more lax GET request), you won't see a querystring. The values are still passed, but not as part of the requested URL. This is a topic you can find tons of information on via Google.
I have a problem with a small app i am writing. Now either my error is in my controller class or its in Routes. See images below.
Controller Class.
This is the default Route i have.
And this is the error i get when i run.
Image not very clear but it says:
The parameters dictionary contains a null entry for parameter 'playerId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Detail(Int32)' in 'GlobalUnited.WebUI.Controllers.PlayerController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
I read through some posts on here, one particularly:
Similar Link
What exactly does he mean when Daniel Renshaw says:
First, I would suggest you use MVC's automatic handling of parameters instead ofpulling them out of the Request yourself. Your controller action has an id parameter which seems to go ignored - use that and add others like it to get the input parameters.
Anyways, after reading that post, i changed my RouteConfig file to:
And Still i got this error. It says:
A route named 'DefaultApi' is already in the route collection. Route names must be unique.
Parameter name: name
I even tried to change my Detail action parameter to: Note the int? declaration
And i got this error after changing:
Is there something i could do to fix this, something less complicated??
All help will be appreciated, thanks.
Your parameter name playerID causes the problem here. ASP.NET MVC can only provide you a parameter named id using the default route. In your case, it cannot map the request to your action because playerID is not nullable or optional. Changing your parameter name to id will solve the problem.
public ActionResult Detail(int id)
You get the second error with route registration because you already have a route named "DefaultApi". You won't need this route if you change the parameter name(also it's registered elsewhere).
Third error is for trying to querying with the null value. ASP.NET MVC cannot map the value in URL to your parameter and you get the default value null. Since there isn't a row with a null value you get an empty sequence, then Single() method throws an exception.
As your parameter is called playerId you must pass it as a route value to the Detail action method and then check if it's null or not. This will take care of that last error you show in your question: Sequence contains no element.
Do this in the action method:
public ActionResult Detail(int? playerId)
{
if(playerId.HasValue)
{
var model = _dataSource.Players.Single(p => p.PlayerId == playerId);
return View(model);
}
// Handle the other possibility where playerId is NULL
}
For ASP.NET MVC to know how to do the correct parameter binding you must call the above action method this way, for example:
#Html.ActionLink("Player Details", "Detail", new { playerId = 1 });
Other than the duplicate route error, your errors have nothing to do with the DefaultApi route. You get a duplicate route because that route is configured in App_Start\WebApiConfig.cs
Your first problem is that you are telling MVC that you have a mandatory Route parameter called playerId, but you are not supplying this route parameter in the URL you're using. In order for this to work you would need to either alter your route to change id to playerId, or add a playerId querystring parameter to your url. If you alter the route, you would need a url like http://my.site/Player/Detail/1 (if you alter the route) or http://my.site/Player/Detail?playerId=1.
Another option is changing the parameter to public ActionResult Detail(int id) which would then use the existing default route that takes a single parameter called id and extracts it from the friendly url that ends in /1.
You could also make the method Detail(int? id), but then you would need to place a null guard around your linq query (because if you don't pass the ID on the URL it can't lookup a null record) so you would have to add this:
if (id.HasValue) {
// execute linq query
}
Alternatively, you could alter your query to return SingleOrDefault() rather than Single().
I've been having some problems getting redirects after login to work how I want. So I came up with the idea to store the current page in the viewbag and use that to redirect, so if the page is mydomain.com/debate/1 I end up with "/debate/1" stored in the viewbad but when I try to redirect its giving me this complaint
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult DebateDetails(Int32)' in 'PoliticalDebate.Controllers.DebateController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
However If I manually type in mydomain.com/Debate/1 it works as expected.
Is there some way to get Redirect to work how I want ?
Since I don't see any code, I can't comment on the way you are trying to do it (which isn't working). On future posts, please post your code. This is one way how you can redirect if you are simply redirecting to the default action on the controller:
return this.RedirectToAction("Index", new { id = debateDetailsID } );
Although it's very hard to tell what you are truly trying to do because you mention debate/1 yet the method being called is DebateDetails which doesn't match (unless you've changed the default routes, again I don't know, there's no code).
Update
According to your comment, you have an error in your MapRoute. Your MapRoute should look like:
routes.MapRoute("Debate Details",
"debate/{id}",
new { controller = "Debate",
action = "DebateDetails",
// this id value is missing
// so it's not being passed to the controller
id = UrlParameter.Optional } );
the answer is there in the complaint, in this particular case you're sending a parameter so, checkout if this is specified
your code must look like redirectToAction("nameOfAction", new {id = yourIdOnViewBag}