Parameters for Controller from Html.ActionLink - c#

On the Home View I'm generating a number of links:
<ul class="nav navbar-nav">
#foreach (var item in ViewBag.RegList)
{
<li>#Html.ActionLink((string)item.registryName, "Index", "Registry", new { item.registryName }, new { item.registryID }) </li>
}
</ul>
I don't get - how do I set params in ActionLink for my controller and where they go from there?
That's how I defined Index in me controller:
public async Task<ActionResult> Index(object attr)
But in attr goes only object, which when casted to string becomes null. If the orgignal type is string - then also null.
How do I transfer a parameter? Or I'm casting value to the wrong type?
Also I don't understand - what must fourth parameter of ActionLink (routeValues) be?
Method that I'm using: https://msdn.microsoft.com/en-us/library/dd504972(v=vs.108).aspx
Or this one: https://msdn.microsoft.com/en-us/library/dd493068(v=vs.108).aspx

If you look at the created link they will contain a set of query string parameters
"https://some.where/controller/SomeAction/7788?extraParam1=foo&extraParam2=bar"
The standard routing config of MVC makes the parameter "id" part of the local path ("7788" in the example), whereas additional parameters are added to the query string (after the question mark).
Your method signature to the action should be something like
public async Task<ActionResult>
SomeAction(string id, string extraParam1, string extraParam2)
to get the parameters in my example link.

Related

Passing multiple multivalued parameters to a MVC controller from different search form

I have a website with two search forms, both calling the same controller and action. First form is for cars, second for motorcycles. Each form has multiple search filters. These filters are multivalued.
My route file:
routes.MapRoute("Cars",
"search/cars",
new { controller = "Search", action = "Index", SearchType = "Cars", Param1 = "", Param2="" }, null);
routes.MapRoute("Motorcycles",
"search/moto",
new { controller = "Search", action = "Index", SearchType = "Moto", Param3 = "", Param4="" }, null);
So calling "mywebsite.com/search/cars?Param1=BMW&Param1=VW" should get me these values into my controller:
SearchType = "Cars"
Param1[] = {"BMW", "VW"}
Is there any way to avoid having action in Search controller declared as:
public ActionResult Index(string SearchType, string Param1, string Param2, string Param3, string Param4){}
But instead have one params[] variable which would then contain key value pairs? Parameters in both cases have different names, so I can't always use the same name. Also each search page has different number of parameters.
You may want to consider using JSON and doing a POST with a JSON object. If you're method is going to take a variety of parameters to the point where it could have 3-4+ params, then you should reconsider how the data is transferred. Perhaps create a model for the search that has the filters as fields? You could then just accept the model as an object and handle it that way.
You should also consider using two different actions on the Search controller. Even though you're "searching", you've stated that each form handles different data, which means you will probably need to handle the search differently. Maybe use a Cars action and a Moto action?

Extend #Url.Action to pass multiple actions

I'm attempting to use #Url.Action to pass multiple parameters through the URL in such a way I can read in the extra parameters using the RouteAttribute
<li><a class="survey-button" href="
#Url.Action("Survey",(Model.SurveyID).ToString(),"Page"(Model.PageNumber).ToString())
">Previous</a></li>
//localhost:8080/Survey/1/Page/1
[Route("Survey/{surveyID}/Page/{pageNumber}")]
public ActionResult Page(int surveyID, int pageNumber)
{
...
I realize I can pass these via a query string, but for cleanliness reasons this is not preferred.
You need to wrap your parameters in a RouteValueDictionary or an anonymous object (easier). I am assuming that your controller name is SurveyController.
#Url.Action("Page", "Survey", new {surveyId = Model.SurveyId, pageNumber = Model.PageNumber})

MVC Routing Null parameters error

On my Index page I have the following link to the Details view:
#Html.ActionLink("Details", "Details", new { id = item.ClubId })|
My controller is expecting an int:
public ActionResult Details(int ClubId)
{
var club = _service.GetClub(ClubId);
var model = AutoMapper.Mapper.Map<ClubViewModel>(club);
return View(model);
}
Im getting this every time though:
The parameters dictionary contains a null entry for parameter 'ClubId'
of non-nullable type 'System.Int32' for method
'System.Web.Mvc.ActionResult Details(Int32)' in
'MyProject.Web.Controllers.ClubsController'. An optional
parameter must be a reference type, a nullable type, or be declared as
an optional parameter. Parameter name: parameters
I know this is something to do with routing however I have tried swapping UrlParameter.Optional to "" and making the ViewModel's ClubId nullable but the error remains.
If I rewire my controller to accept a Club object and pass in item from the Index view then everything is fine and the ClubId is populated in debug but I'm left with a stupidly large parameter list in the URL.
I don't really get what the problem is here?
Your controller is expecting a parameter named ClubId but you're passing a parameter called id. They need to match.
have you tried using ClubId instead of just id?

How do I accept an array as an ASP.NET MVC controller action parameter?

I have an ASP.net MVC controller called Designs that has an action with the following signature:
public ActionResult Multiple(int[] ids)
However, when I try to navigate to this action using the url:
http://localhost:54119/Designs/Multiple?ids=24041,24117
The ids parameter is always null. Is there any way to get MVC to convert the ?ids= URL query parameter into an array for the action? I've seen talk of using an action filter but as far as I can tell that will only work for POSTs where the array is passed in the request data rather than in the URL itself.
The default model binder expects this url:
http://localhost:54119/Designs/Multiple?ids=24041&ids=24117
in order to successfully bind to:
public ActionResult Multiple(int[] ids)
{
...
}
And if you want this to work with comma separated values you could write a custom model binder:
public class IntArrayModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (value == null || string.IsNullOrEmpty(value.AttemptedValue))
{
return null;
}
return value
.AttemptedValue
.Split(',')
.Select(int.Parse)
.ToArray();
}
}
and then you could apply this model binder to a particular action argument:
public ActionResult Multiple([ModelBinder(typeof(IntArrayModelBinder))] int[] ids)
{
...
}
or apply it globally to all integer array parameters in your Application_Start in Global.asax:
ModelBinders.Binders.Add(typeof(int[]), new IntArrayModelBinder());
and now your controller action might look like this:
public ActionResult Multiple(int[] ids)
{
...
}
To extend on Darin Dimitrov's answer, something you can get away with is accepting a simple string in your URL parameter and converting it to an array yourself:
public ActionResult Multiple(string ids){
int[] idsArray = ids.Split(',').Select(int.Parse).ToArray();
/* ...process results... */
}
If you get a parse error while doing this (because someone passed you a malformed array), you can cause your exception handler to return a 400 Bad Request error instead of the default, more unfriendly 404 Not Found error that MVC returns when an endpoint is not found.
You can also use this URL format, and ASP.NET MVC will do everything for you. But, remember to apply URL encoding.
?param1[0]=3344&param1[1]=2222
I don't know where Groky's URL string was coming from, but I had the same problem with some javascript calling my controller/action. It would build up a URL of null, 1, or many "IDs" from a multiple-select list (which is unique to the solution I'm going to share).
I copy/pasted Darin's custom model binder and decorated my action/parameter, but it didn't work. I still got null valued int[] ids. Even in the "safe" case where I actually did have many IDs.
I ended up changing the javascript to produce an ASP.NET MVC friendly parameter array like
?ids=1&ids=2
I had to do some silly stuff, though
ids || [] #=> if null, get an empty array
[ids || []] #=> if a single item, wrap it in an array
[].concat.apply([], ...) #=> in case I wrapped an array, flatten it
So, the full block was
ids = [].concat.apply([], [ids || []])
id_parameter = 'ids=' + ids.join('&ids=')
It's messy, but it's the first time I had to hack like this in javascript.
.Net Core Answer
For those coming here in recent times, you can do this in .Net Core with:
http://localhost:54119/Designs/Multiple?ids=24041&ids=24117
and:
public ActionResult Multiple([FromQuery] int[] ids)
{
...
}

What is wrong with my route?

I get the following error when I click the Edit link from the List view
The parameters dictionary contains a null entry for parameter 'envId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Edit(Int32)' in 'WebUI.Controllers.EnvironmentsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
Here is my code:
Summary.ascx
Routes
Env Controller, Edit Action methods
Env Controller, List Action method
EnvRepository and SqlEnvRepository
Your auto-generated links say this:
<td><%= Html.ActionLink("Edit", "Edit", new { id= Model.EnvironmentID} )%></td>
but the controller code says this:
public ActionResult Edit(int envId)
MVC's model binding hooks the parameters in the action up by name, and the default route assumes the first parameter will be an int called id. Change the name of your Edit() parameter to id and it should work.
Alternatively, you could change the ActionLink parameters object to new { envId = Model.EnvironmentID } but that will cause your URLs to look like this:
http://localhost/Env/Edit?envId = 1
instead of this:
http://localhost/Env/Edit/1

Categories

Resources