Return url by routename and routevalues - asp.net-mvc

I'm trying to programatically generate a link by routename and routevalues. I store the routes and values in the database.
How can I adjust this helper in order to make it work?
public static string GenerateLink(this HtmlHelper helper, string routeName, Dictionary<string, string> parameters)
{
// ??
RouteValueDictionary rd = new RouteValueDictionary();
foreach (var item in parameters)
{
rd.Add(item.Key, item.Value);
}
// string url = ??
return url;
}
to use it like:
<%= Html.GenerateLink(Model.SomeLinkName, Model.RouteName, ..?) %>
/M

Actually this method already exists for you.
Html.RouteLink(string LinkText, string routeName, RoutevalueDictionary routeValues)
The only thing you need to do is turn your IDictionary into a RouteValueDictionary which again is quite simple as the constructor for a RVD can take an IDictionary which saves you from doing the foreach loop in your example.
So finally all you need is
Html.RouteLink(string LinkText, string routeName, new RoutevalueDictionary(parameters) )

Related

How to build custom URL link MVC

There are some methods like the followings in UrlHelper class in MVC4
public string Action(string actionName, object routeValues);
public string Action(string actionName, RouteValueDictionary routeValues);
...but i want to change route values in every Action method calls
for this I wrote CustomUrlHelper class like the following:
public class CustomUrl : UrlHelper
{
public CustomUrl()
: base(HttpContext.Current.Request.RequestContext,RouteTable.Routes){}
public string CustomAction(string actionName, string controllerName, string areaName, object routeValues, bool generateToken)
{
RouteValueDictionary rvd = new RouteValueDictionary(routeValues);
//some changes on route value dictionary
return Action(actionName,controllerName, rvd);
}
}
If we know action name controller name than every thing is OK, but if we just know a base URL and route value dictionary than my CustomAction method does not work.
Let's say if I have just a linkbase like A/B/C/D and RouteValueDictionary.
Now I want a method it gets baseUrl and RouteValueDictionary and produces complete link.

Routes values not passing in Html.ActionLink attribute in MVC 4

I want to pass an id like a string to my action method , I am doing like this
<li>#Html.ActionLink("Nokia Lumia Series", "Mobiles", "Products", new { id = "Lumia" })
and here is my action method
public ActionResult Mobiles(string name)
{
return View();
}
but it is not going on that action and this url is shown in browser tab
http://mymobiles.com/Home/Mobiles?Length=8
What I am doing wrong here??
You are using an incorrect overload of ActionLink. You should use this overload:
public static MvcHtmlString ActionLink(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName,
Object routeValues,
Object htmlAttributes
)
Your code then becomes, I also changed your parameter name to make it more clear:
#Html.ActionLink("Nokia Lumia Series", "Mobiles", "Products", new { name = "Lumia" }, null)

How to enable the addition of an extra parameter to URL.Action via an Extension method

I am trying to understand how I can add an extra parameter to URL.Action, and have it as part of the resultant link.
Lets assume the following:
myParm = "myTestParameterValue";
#Url.Action("Edit", "Order", new { id=item.Id}, null,myParm)
which would result in:
/Order/Edit/1/myTestParameterValue
I would really appreciate some sample code of the extension method for this Action Sample to see how the parameters are taken in and how the link is generated.
I guess it would start something like:
public static MvcHtmlString Action(this HtmlHelper helper, string actionName, string controllerName, object routeValues, boolean IsHashRequired)
If (IsHashRequired)
{
String myHash = GetHash();
}
// Pseudocode .... string myNewLink = ... + myHash
Many thanks in advance
EDIT
I need to calculate hash to add to resultant link. A better parameter would be a boolean. I have edited code accordingly.
EDIT2:
public static IHtmlString Action(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, string protocol, bool isHashRequired )
{
if (isHashRequired)
{
routeValues["hash"] = "dskjdfhdksjhgkdj"; //Sample value.
}
return urlHelper.Action(???); // Resultant URL = /Order/Edit/1/dskjdfhdksjhgkdj
}
EDIT3:
Struggling with :
return urlHelper.Action(actionName, controllerName, routeValues, protocol);
Apparently needs converting to IHtmlString??
EDIT4:
public static String Action(this UrlHelper urlHelper, string actionName, string controllerName, object routeValues, string protocol, bool isHashRequired )
{
RouteValueDictionary rvd = new RouteValueDictionary(routeValues);
if (isHashRequired)
{
string token = "FDSKGLJDS";
rvd.Add("urltoken", token);
}
return urlHelper.Action(actionName, controllerName, rvd, protocol); //rvd is incorrect I believe
}
EDIT5
return urlHelper.Action(actionName, controllerName, rvd, protocol,null);
where
rvd is the RouteValueDictionary
hostname is null.
Thanks...
You should consider modifying your routes
Where you have your routing configured add something like this:
routes.MapRoute(
"hash", // Route name
"{controller}/{action}/{id}/{hash}", // URL with parameters
new { controller = "Home", action = "Index", id = "", hash = "" } // Parameter defaults
);
And use URL.Action like this:
myParm = "myTestParameterValue";
#Url.Action("Edit", "Order", new { id=item.Id, hash = myParm}, null);
You can easily add this with a new extension method class
public static class MyExtensions
{
public static IHtmlString ActionWithHash(this UrlHelper urlHelper, ....)
{
if (hashRequired)
{
routeParameters["hash"] = ...
}
return urlHelper.Action(...);
}
}

ASP.NET MVC, Querystring, routes and default binder, how do I combine this?

I have an ASP.NET MVC site that uses strongly typed views. In my case, a controller action could look like this:
public ActionResult List(MyStrongType data)
When submitting the page (view) the response will generate a URL that looks something like this (yes, I know routing could generate a nicer URL):
http://localhost/Ad/List?F.ShowF=0&ALS.CP=30&ALS.L=0&ALS.OB=0&ALS.ST=0&S=&LS.L1=&LS.L2=&CS.C1=32&CS.C2=34&CS.C3=&ALS.ST=0
If I submit the page again, I can see that the data object in the action is set properly (according to the URL)(default binder).
The problem is: Say that I am to add page buttons (to change the page) for a list on my sitepage, the list will be controlled by settings like filter, sortorder, amount of pages per page and so on (controlled by the querystring). First, I need to include all current query parameters in the URL, and then I need to update the page parameter without tampering with the other query parameters. How can I genereate this URL from the view/"HTML helper"?
I could of course manipulate the URL string manually, but this will involve a lot of work and it will be hard to keep up to date if a route is changed, there must be a easier way? Like some kind of querystring collection that can be altered on service side (like ASP.NET Request.QueryString)?
I would hope to not involve the route, but I post the one I got so far anyway:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"TreeEditing",
"{controller}/{action}/{name}/{id}",
new { controller = "MyCategory", action = "Add", name = string.Empty, id = -1 }
);
BestRegards
Edit 1: It's possible to set the query parameters like this (in view):
<%= url.Action(new {controller="search", action="result", query="Beverages", Page=2})%>
But this will only generate a URL like this (with the default route):
/search/result?query=Beverages&page=2
The rest of the parameters will be missing as you can see.
I could of course add every known parameter in this URL action, but if any query parameter is added or changed there will be a lot of work to keep everything up to date.
I have read the article ASP.NET MVC Framework (Part 2): URL Routing, but how do I find an answer to my problem?
It sounds to me like the problem you have is that you want to be able to easily persist query string values from the current request and render them into the URLs of links in your view. One solution would be to create an HtmlHelper method that returns the existing query string with some changes. I created an extension method for the HtmlHelper class that takes an object and merges its property names and values with the query string from the current request, and returns the modified querystring. It looks like this:
public static class StackOverflowExtensions
{
public static string UpdateCurrentQueryString(this HtmlHelper helper, object parameters)
{
var newQueryStringNameValueCollection = new NameValueCollection(HttpContext.Current.Request.QueryString);
foreach (var propertyInfo in parameters.GetType().GetProperties(BindingFlags.Public))
{
newQueryStringNameValueCollection[propertyInfo.Name] = propertyInfo.GetValue(parameters, null).ToString();
}
return ToQueryString(newQueryStringNameValueCollection);
}
private static string ToQueryString(NameValueCollection nvc)
{
return "?" + string.Join("&", Array.ConvertAll(nvc.AllKeys, key => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(nvc[key]))));
}
}
It will loop through the query string values from the current request and merge in the properties defined on the object you passed in. So your view code might look like this:
<a href='/SomeController/SomeAction<%=Html.GetCurrentQueryStringWithReplacements(new {page = "2", parameter2 = "someValue"})%>'>Some Link</a>
This is basically saying "keep the query string from the current request, but change the page and parameter2 values, or create them if they didn't exist." Note that if your current request has a "page" query string parameter, this method will overwrite the value from the current request with the one you explicitly pass in from the view.
In this case, if your querystring was:
?parameter1=abc&page=1
It would become:
?parameter1=abc&page=2&parameter2=someValue
EDIT:
The above implementation will probably not work with the dictionary lookup of querystring parameter names you described. Here is an implementation and that uses a dictionary instead of an object:
public static string UpdateCurrentQueryString(this HtmlHelper helper, Dictionary<string, string> newParameters)
{
var newQueryStringNameValueCollection = new NameValueCollection(HttpContext.Current.Request.QueryString);
foreach (var parameter in newParameters)
{
newQueryStringNameValueCollection[parameter.Key] = parameter.Value;
}
return ToQueryString(newQueryStringNameValueCollection);
}
Your view would call the function by doing an inline initialization of a dictionary and passing it to the helper function like this:
<a href='/SomeController/SomeAction<%=Html.GetCurrentQueryStringWithReplacements(new Dictionary<string,string>() {
{ QuerystringHandler.Instance.KnownQueryParameters[QuaryParameters.PageNr], "2" },
{ QuerystringHandler.Instance.KnownQueryParameters[QuaryParameters.AnotherParam], "1234" }})%>'>Some Link</a>
I did JUST what you need!
I created an HTML helper for this. The helper takes the same parameters as a normal helper. Yet, it keeps the current values from the URL. I made it both for the URL helper as a ActionLink helper.
This replaces the: Url.Action()
<a href='<%= Html.UrlwParams("TeamStart","Inschrijvingen", new {modID=item.Mod_ID}) %>' title="Selecteer">
<img src="<%= Url.Content("~/img/arrow_right.png") %>" alt="Selecteer" width="16" /></a>
and this replaces the Html.ActionLink()
<%: Html.ActionLinkwParams("Tekst of url", "Action", new {test="yes"}) %>
Here is the helper:
using System;
using System.Web.Mvc;
using System.Web.Routing;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Web.Mvc.Html;
namespace MVC2_NASTEST.Helpers {
public static class ActionLinkwParamsExtensions {
public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, string controller, object extraRVs, object htmlAttributes) {
NameValueCollection c = helper.ViewContext.RequestContext.HttpContext.Request.QueryString;
RouteValueDictionary r = new RouteValueDictionary();
foreach (string s in c.AllKeys) {
r.Add(s, c[s]);
}
RouteValueDictionary htmlAtts = new RouteValueDictionary(htmlAttributes);
RouteValueDictionary extra = new RouteValueDictionary(extraRVs);
RouteValueDictionary m = RouteValues.MergeRouteValues(r, extra);
//return System.Web.Mvc.Html.LinkExtensions.ActionLink(helper, linktext, action, controller, m, htmlAtts);
return helper.ActionLink(linktext, action, controller, m, htmlAtts);
}
public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action) {
return ActionLinkwParams(helper, linktext, action, null, null, null);
}
public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, string controller) {
return ActionLinkwParams(helper, linktext, action, controller, null, null);
}
public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, object extraRVs) {
return ActionLinkwParams(helper, linktext, action, null, extraRVs, null);
}
public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, string controller, object extraRVs) {
return ActionLinkwParams(helper, linktext, action, controller, extraRVs, null);
}
public static MvcHtmlString ActionLinkwParams(this HtmlHelper helper, string linktext, string action, object extraRVs, object htmlAttributes) {
return ActionLinkwParams(helper, linktext, action, null, extraRVs, htmlAttributes);
}
}
public static class UrlwParamsExtensions {
public static string UrlwParams(this HtmlHelper helper, string action, string controller, object extraRVs) {
NameValueCollection c = helper.ViewContext.RequestContext.HttpContext.Request.QueryString;
RouteValueDictionary r = RouteValues.optionalParamters(c);
RouteValueDictionary extra = new RouteValueDictionary(extraRVs);
RouteValueDictionary m = RouteValues.MergeRouteValues(r, extra);
string s = UrlHelper.GenerateUrl("", action, controller, m, helper.RouteCollection, helper.ViewContext.RequestContext, false);
return s;
}
public static string UrlwParams(this HtmlHelper helper, string action) {
return UrlwParams(helper, action, null, null);
}
public static string UrlwParams(this HtmlHelper helper, string action, string controller) {
return UrlwParams(helper, action, controller, null);
}
public static string UrlwParams(this HtmlHelper helper, string action, object extraRVs) {
return UrlwParams(helper, action, null, extraRVs);
}
}
}
How does it work?
The calls are the same as for the Html.ActionLink() so you simple can replace those.
The method does the following:
It takes all the optional parameters from the current URL and places them in a RouteValueDictionary.
It also places the htmlattributes in a dictionary.
Then it takes the extra routevalues which you specified manually and places those in a RouteValueDictionary as well.
The key then is to merge the ones from the URL and the ones manually specified.
This happens in the RouteValues class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using System.Collections.Specialized;
using System.Web.Mvc;
namespace MVC2_NASTEST {
public static class RouteValues {
public static RouteValueDictionary optionalParamters() {
return optionalParamters(HttpContext.Current.Request.QueryString);
}
public static RouteValueDictionary optionalParamters(NameValueCollection c) {
RouteValueDictionary r = new RouteValueDictionary();
foreach (string s in c.AllKeys) {
r.Add(s, c[s]);
}
return r;
}
public static RouteValueDictionary MergeRouteValues(this RouteValueDictionary original, RouteValueDictionary newVals) {
// Create a new dictionary containing implicit and auto-generated values
RouteValueDictionary merged = new RouteValueDictionary(original);
foreach (var f in newVals) {
if (merged.ContainsKey(f.Key)) {
merged[f.Key] = f.Value;
} else {
merged.Add(f.Key, f.Value);
}
}
return merged;
}
public static RouteValueDictionary MergeRouteValues(this RouteValueDictionary original, object newVals) {
return MergeRouteValues(original, new RouteValueDictionary(newVals));
}
}
}
This is all pretty straightforward. In the end, the actionlink is made with the merged routevalues. This code also lets you remove values from the URL.
Examples:
Your URL is localhost.com/controller/action?id=10&foo=bar. If in that page you place this code
<%: Html.ActionLinkwParams("Tekst of url", "Action", new {test="yes"}) %>
the URL returned in that element will be localhost.com/controller/action?id=10&foo=bar&test=yes.
If you want to remove an item, you just set the item as an empty string. For example,
<%: Html.ActionLinkwParams("Tekst of url", "Action", new {test="yes", foo=""}) %>
will return the URL in the <a> element: localhost.com/controller/action?id=10&test=yes
I'm guessing this is all you need?
If you have some additional questions, just ask.
Extra:
Sometimes you will want to keep your values inside your action too, when you will redirect to another Action. With my RouteValues class, this can be done very easily:
public ActionResult Action(string something, int? somethingelse) {
return RedirectToAction("index", routeValues.optionalParamters(Request.QueryString));
}
If you still want to add some optional parameters, no problem!
public ActionResult Action(string something, int? somethingelse) {
return RedirectToAction("index", routeValues.optionalParamters(Request.QueryString).MergeRouteValues(new{somethingelse=somethingelse}));
}
I think that covers pretty much everything you'll need.
If you want to set the query string in a link of a view:
Html.ActionLink("LinkName", "Action", "Controller", new { param1 = value1, param2 = value2 }, ...)
If you want to set it in the browser URL after a post back, just call Route* in an action like RouteToAction() and set the parameter key/value you want.
If you use the action public ActionResult List(MyStrongType data), you need to include all page settings (page index, ordering,...) as parameters to 'MyStrongType', and the data object will contain all infomation for the view.
In the view, if you need to generate a URL, using the approach of CallMeLaNN:
Html.ActionLink("LinkName", "Action", "Controller", new { param1 = Model.value1, param2 = Model.param2, ... });. You need to manually set all the parameters here or create a helper to help you fill the URL.
You don't need to care about current parameters included in the address.
You can route:
routes.MapRoute(
"custome",
"{controller}/{action}/",
new { controller = "Home", action = "Index"}
);
to generate all the parameters as a query string.

HtmlHelper methods and RouteValueDictionary

When writing an htmlhelper extension if I want to support the similarly structured ctors for my htmlhelper extension method, I use RouteValueDictionary as follows:
public static string ListBoxDict(this HtmlHelper htmlHelper,
string name,
object value,
object htmlAttributes)
{
return ListBoxDict(htmlHelper,
name,
value,
((IDictionary<string, object>)
new RouteValueDictionary(htmlAttributes)));
}
My question really is why the need for RouteValueDictionary ... I know you can't just cast the htmlAttributes to IDictionary<string, object> ... though I'm not sure why and that might be where I'm confused. Shouldn't RouteValueDictionary be to do with Routing and therefore nothing to do with HtmlHelper methods? Like I say, I'm probably missing the point so I'd be glad if someone could tell me what I've missed.
Cheers...
edit: in response to Dan's answer -->
I was just following what I had seen in use in the mvc source code for input helpers...
see "src\SystemWebMvc\Mvc\Html\InputExtensions.cs"
It does as follows:
public static string TextBox(this HtmlHelper htmlHelper,
string name,
object value,
object htmlAttributes)
{
return TextBox(htmlHelper,
name,
value,
new RouteValueDictionary(htmlAttributes))
}
Clearly a shortcut but is it a bastardization or is it ok to do it?
I would strongly recommend looking at Rob Conery's blog post about something like this.
The meat and veg of it is this:
Codedump:
public static string ToAttributeList(this object list)
{
StringBuilder sb = new StringBuilder();
if (list != null)
{
Hashtable attributeHash = GetPropertyHash(list);
string resultFormat = "{0}=\"{1}\" ";
foreach (string attribute in attributeHash.Keys)
{
sb.AppendFormat(resultFormat, attribute.Replace("_", ""),
attributeHash[attribute]);
}
}
return sb.ToString();
}
public static string ToAttributeList(this object list,
params object[] ignoreList)
{
Hashtable attributeHash = GetPropertyHash(list);
string resultFormat = "{0}=\"{1}\" ";
StringBuilder sb = new StringBuilder();
foreach (string attribute in attributeHash.Keys)
{
if (!ignoreList.Contains(attribute))
{
sb.AppendFormat(resultFormat, attribute,
attributeHash[attribute]);
}
}
return sb.ToString();
}
public static Hashtable GetPropertyHash(object properties)
{
Hashtable values = null;
if (properties != null)
{
values = new Hashtable();
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(properties);
foreach (PropertyDescriptor prop in props)
{
values.Add(prop.Name, prop.GetValue(properties));
}
}
return values;
}
Usage:
public static string ListBoxDict(this HtmlHelper htmlHelper,
string name,
object value,
object htmlAttributes)
{
return htmlHelper.ListBoxDict(name,
value,
htmlAttributes.ToAttributeList()));
}
What .ToAttributeList() does is convert your htmlAttribute object to
name = "value"
Hope this makes sense.

Resources