Reputation: 513
Don't know why, but my Action method showing null for id. I'm passing id in the url but still its coming null
public ActionResult SpecificEmpDetails(string empId)
{
CustomerBAL customer = new CustomerBAL();
Customer custs = customer.Employees.Single(emp => emp.EmpID.ToString() == empId);
ViewBag.Customers = custs;
return View("EmpDetails");
}
<ul>
<li>
Customer ID : @ViewBag.Customers.EmpID
</li>
<li>
Customer Name : @ViewBag.Customers.EmpName
</li>
<li>
Customer Age : @ViewBag.Customers.EmpAge
</li>
<li>
Customer Gender : @ViewBag.Customers.EmpGender
</li>
<li>
Customer Salary : @ViewBag.Customers.EmpSalary
</li>
</ul>
public class CustomerBAL : DbContext
{
public DbSet<Customer> Employees { get; set; }
}
[Table("tblEmployee")]
public class Customer
{
[Key]
public int EmpID { get; set; }
public string EmpName { get; set; }
public string EmpGender { get; set; }
public int EmpAge { get; set; }
public int EmpSalary { get; set; }
}
routes.MapRoute(
name: "SpecificUser",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Test", action = "SpecificEmpDetails", id = UrlParameter.Optional }
);
localhost:8983/Test/SpecificEmpDetails/1
Id is coming as null, but when I'm changing the ID(in the watch window) during debugging from null to any available id, its showing the required employee data. I'm totally confused why id is not getting passed to the controller action method from the url.
Upvotes: 1
Views: 2783
Reputation: 56849
There are 2 different things going on here.
First of all, your route definition is:
routes.MapRoute(
name: "SpecificUser",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Test", action = "SpecificEmpDetails", id = UrlParameter.Optional }
);
You are specifying that the name of the route value that is passed is {id}
. Therefore, any URL that matches this route will be entered into the URL table with the route key of id
and value that is passed in the URL.
For example, with the URL /Test/SpecificEmpDetails/1
, your route table looks like this:
| Key | Value |
|-------------|---------------------|
| controller | Test |
| action | SpecificEmpDetails |
| id | 1 |
The reason it looks this way is because you are passing the values in the URL, and they are being converted into route values via the placeholders you defined.
The way MVC gets the values to the action method parameters (or technically, any value of a Model) is through value providers.
Value providers lookup the key from several places, such as query string parameters, form values, and route values to try to match the key names with what are in each of those dictionaries. If the parameter name of the action method matches, it will pass the value through. If there is no match, the value will be the default value of the data type.
You can see the value providers that are registered by default (and what order of precedence they have) by looking at the static ValueProviderFactories.Factories
property. You can also remove and add/insert custom value provider factories to this property.
Therefore, with a custom value provider, you could make one name map to another name if you really wanted to. In this case, you could make the request for the key empId
match the route value id
.
That said, I recommend you do one of the following.
The route you have provided won't work in the same MVC application as the default route. The reason is that it will match any URL that has 0, 1, 2, or 3 segments just like the default route. If a URL of this length is passed to the application, it will match the first route registered and will ignore the other. See Why map special routes first before common routes in asp.net mvc? for a complete explanation.
So, if you just use one route in your application, and use id
as both your route key and action method parameter, everything will flow accordingly and will be simpler than using a custom route.
IMO, it probably doesn't make much sense to make the id
parameter optional for this particular URL. You have no code in your action method to deal with an empty id
value anyway. Note however if you pass the URL /Test/SpecificEmpDetails
, it will still match the Default
route and match your controller action anyway (with an empty value). To avoid this, you can insert an IgnoreRoute
to block the URL from matching in this case.
// Ignore the URL if it is passed with no id, so you get a 404 not found
routes.IgnoreRoute("Test/SpecificEmpDetails");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
And your action method:
public ActionResult SpecificEmpDetails(string id)
{
CustomerBAL customer = new CustomerBAL();
Customer custs = customer.Employees.Single(emp => emp.EmpID.ToString() == id);
ViewBag.Customers = custs;
return View("EmpDetails");
}
If you want a custom route, you must constrain the route so it will not match every value that is passed. In this case, you only want it to match /Test/SpecificEmpDetails/<SomeID>
, so the simplest option is just to use literal segments instead of placeholders.
You also need to place your custom route first before the default route or it will be an unreachable execution path.
As above, it will still match the Default
route and match your controller action anyway (with a null
value, since id
is not equal to empId
). So you should also insert an IgnoreRoute
to block the URL from matching in this case.
// Ignore the URL if it is passed with no empId, so you get a 404 not found
routes.IgnoreRoute("Test/SpecificEmpDetails");
routes.MapRoute(
name: "SpecificUser",
url: "Test/SpecificEmpDetails/{empId}",
defaults: new { controller = "Test", action = "SpecificEmpDetails" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Then you can use your method as you have it in your question.
public ActionResult SpecificEmpDetails(string empId)
{
CustomerBAL customer = new CustomerBAL();
Customer custs = customer.Employees.Single(emp => emp.EmpID.ToString() == empId);
ViewBag.Customers = custs;
return View("EmpDetails");
}
Upvotes: 2
Reputation: 14995
The Id
in Url will not pass into empId
becasuse in the MapRoute the number after action will goes to Id
not empId
Change your action like this and everything will works fine.
public ActionResult SpecificEmpDetails(string Id)
{
CustomerBAL customer = new CustomerBAL();
Customer custs = customer.Employees.Single(emp => emp.EmpID.ToString() == Id);
ViewBag.Customers = custs;
return View("EmpDetails");
}
If you don't want to change your method, you must use this url in order to fix it :
localhost:8983/Test/SpecificEmpDetails?empId=1
Upvotes: 1