Look on my code that i created in a partial View:
<% foreach (Customer customerInfo in Model.DataRows) {%>
<tr>
<td>
<%=Html.ActionLink(
customerInfo.FullName
, ((string)ViewData["ActionNameForSelectedCustomer"])
, JoinParameters(customerInfo.id, (RouteValueDictionary) ViewData["AdditionalSelectionParameters"])
, null)%>
</td>
<td>
<%=customerInfo.LegalGuardianName %>
</td>
<td>
<%=customerInfo.HomePhone %>
</td>
<td>
<%=customerInfo.CellPhone %>
</td>
</tr>
<%}%>
Here I'm building simple table that showing customer's details.
As you may see, in each row, I'm trying to build a link that will redirect to another action.
That action requires customerId and some additional parameters.
Additional parameters are different for each page where this partial View is using. So, i decided to make Action methods to pass that additional parameters in the ViewData as RouteValueDictionary instance.
Now, on the view i have a problem, i need to pass customerId and that RouteValueDictionary together into Html.ActionLink method. That makes me to figure out some way of how to combine all that params into one object (either object or new RouteValueDictionary instance)
Because of the way the MVC does, i can't create create a method in the codebehind class (there is no codebihind in MVC) that will join that parameters.
So, i used ugly way - inserted inline code:
...script runat="server"...
private RouteValueDictionary JoinParameters(int customerId, RouteValueDictionary defaultValues)
{
RouteValueDictionary routeValueDictionary = new RouteValueDictionary(defaultValues);
routeValueDictionary.Add("customerId", customerId);
return routeValueDictionary;
}
...script...
This way is very ugly for me, because i hate to use inline code in the View part.
My question is - is there any better way of how i can mix parameters passed from the action (in ViewData, TempData, other...) and the parameter from the view when building action links.
May be i can build this link in other way ?
Thanks!
Write an extension method to merge source dictionary into the destination.
public static class Util
{
public static RouteValueDictionary Extend(this RouteValueDictionary dest, IEnumerable<KeyValuePair<string, object>> src)
{
src.ToList().ForEach(x => { dest[x.Key] = x.Value; });
return dest;
}
public static RouteValueDictionary Merge(this RouteValueDictionary source, IEnumerable<KeyValuePair<string, object>> newData)
{
return (new RouteValueDictionary(source)).Extend(newData);
}
}
usage:
Url.Action(actionName, controllerName, destRvd.Extend(srcRvd));
If you instead want a 3rd dictionary containing the contents of the two - use Merge. But that is less efficient. This approach enables more or less efficient chaining of the merges if you need to merge more than 2 dictionaries:
Url.Action(actionName, controllerName, destRvd.Extend(srcRvd1).Extend(srcRvd2));
Efficiently merge 3 dictionaries into a new one:
Url.Action(actionName, controllerName, srcRvd1.Merge(srcRvd2).Extend(srcRvd3));
Yes - you can merge two (or more) RouteValueDictionary
objects together using the Union method. Microsoft has good example code of the Union method here but Microsoft isn't really clear when it comes to using RouteValueDictionary Unions (hence the reason for my post).
Given the following classes...
public class Student
{
public int StudentID { get; set; }
public string FirstName { get; set; }
public string LastNamse { get; set; }
public string Email { get; set; }
public DateTime DateOfBirth { get; set; }
}
public class Address
{
public string StreetLine1 { get; set; }
public string StreetLine2 { get; set; }
public string Suburb { get; set; }
public string State { get; set; }
public string PostCode { get; set; }
}
...and some sample code...
Student aStudent = new Student();
aStudent.StudentID = 1234;
aStudent.FirstName = "Peter";
aStudent.LastNamse = "Wilson";
aStudent.Email = "[email protected]";
aStudent.DateOfBirth = new DateTime(1969, 12, 31);
Address homeAddr = new Address();
homeAddr.StreetLine1 = "Unit 99, Floor 10";
homeAddr.StreetLine2 = "99-199 Example Street";
homeAddr.Suburb = "Sydney";
homeAddr.State = "NSW";
homeAddr.PostCode = "2001";
Initialize and add the KeyValue<string, object>
elements to the RouteValueDictionary
objects:
RouteValueDictionary routeStudent = new RouteValueDictionary(aStudent);
RouteValueDictionary routeAddress = new RouteValueDictionary(homeAddr);
...Merge the two dictionary objects using the Union operator:
RouteValueDictionary routeCombined = new RouteValueDictionary(routeStudent.Union(routeAddress).ToDictionary(k => k.Key, k => k.Value));
You can now pass the routeCombined variable to the appropriate method (such as ActionLink).
@Html.ActionLink("Click to pass routeValues to the RouteAPI", "MyActionName", routeCombined);
Writing out to the debug window...
foreach (KeyValuePair<string, object> n in routeCombined)
System.Diagnostics.Debug.WriteLine(n.Key + ": " + n.Value);
...will produce the following:
StudentID: 1234 FirstName: Peter LastNamse: Wilson Email: [email protected] DateOfBirth: 31/12/1969 12:00:00 AM StreetLine1: Unit 99, Floor 10 StreetLine2: 99-199 Example Street Suburb: Sydney State: NSW PostCode: 2001
Perhaps a simple way to do accomplish this would be to create a UrlHelper extension that does something similar to what your JoinParameters method does. Something like the method below is a bit more reusable:
public static RouteValueDictionary AppendRouteValue(this UrlHelper helper, RouteValueDictionary routeValues, string key, object value)
{
RouteValueDictionary routeValueDictionary = new RouteValueDictionary(routeValues);
routeValueDictionary.Add(key, value);
return routeValueDictionary;
}
Now your action like would be something like:
<%=Html.ActionLink(
customerInfo.FullName
, ((string)ViewData["ActionNameForSelectedCustomer"])
, Url.AppendRouteValue((RouteValueDictionary) ViewData["AdditionalSelectionParameters"], "customerId", customerInfo.id)
, null)%>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With