How to dynamically create routes in a HTML actionlink - c#

I am trying to send the route values to a method but I cant seem to figure this out. Here is my code
<% string s = "cool";
object d = new { s = "1" };
%>
<%= Html.ActionLink("Home", "Index", d, "ql")%>
The following code produces a url like this
http://localhost:49450/?s=1
the url should be like this
http://localhost:49450/?cool=1
What am I missing

because in the context of a 'new { ... }' expression the 's' does not correspond to a variable as it may first appear - it defines the name of a member of an anonymous class that is created.
when you say :
new { S = 123 }
you are actually generating a class, which is anonymous (you never get to see the name of the class). The type of each member of the class is implicitly determined by whatever you're assigning to it. In the above example a class something like this is generated
class AnonymousClass_S483Ks4 {
public int S {get;set;}
}
There are two ways you can do what you want:
1) you would have to say :
new { cool = 123 }
2)
Now I assume though that you want the name to be dynamic so you need to use RouteValueDictionary which allows you to put key value pairs in.
// RouteValueDictionary is IDictionary<string, object>
var dictionary = new RouteValueDictionary();
string s = "cool";
dictionary.Add(s, 123);
htmlHelper.ActionLink("Home", "Index", dictionary);
As you can see, here you can use a variable 's' to represent whatever you want. This should give you the URL you need.

Related

How to create keyless URL parameters in ASP.NET

I have seen this answer describing ASP.NET support for keyless (not valueless) parameters, like http://some.url?param1&param2, and confirmed them to be viewable on Request.QueryString like:
var values = this.Request.QueryString.GetValues(null);
values.Any(o => o == "param1");
This is fine and dandy but now I want to generate urls like this. My first intuition was to use the RouteValueDictionary: routeValues parameter of Url.Action with null as a key:
#{
var dict = new RouteValueDictionary();
dict.Add(null, "param1");
dict.Add(null, "param2");
}
Very link, amaze
But apparently C# forbids nulls as dictionary keys because of reasons.
I have also tried the empty string as the key, but it results in a query string like: ?=param1,=param2 which contains 2 more equal signs that I want it to.
Of course I can string manipulate the heck out of my URL and add the &param1 part to the query string, but I was hoping for a concise solution.
You want to add the key values, but leaving the value null isn't allowed.
RouteValueDictionary ignores empty values
You could add a value like 1 for instance, but you lose your fine and dandy solution.
#{
var dict = new RouteValueDictionary();
dict.Add("param1",1);
}
Very link, amaze
For another solution you will have to write some custom code.
Since there's no built-in helper for this why don't you roll your own:
public static class UrlHelperExtensions
{
public static string MyAction(this UrlHelper urlHelper, string actionName, IList<string> parameters)
{
string url = urlHelper.Action(actionName);
if (parameters == null || !parameters.Any())
{
return url;
}
return string.Format("{0}?{1}", url, string.Join("&", parameters));
}
}
and then:
#{
var parameters = new List<string>();
parameters.Add("param1");
parameters.Add("param2");
}
#Url.MyAction("ActionName", parameters)

Add automatically anonymous properties to routeValues which is object in ASP.NET MVC4 ActionLink

I use this ActionLink method in order to generate method.
LinkExtensions.ActionLink Method (HtmlHelper, String, String, Object)
Sot the 4th parameter is an Object which contains anonymous properties, used for routes.
It is possible to append/add automatically new anonymous properties to existing routeValues which is Object ?
If yes, how ?
Let's assume that I have a method:
public void Test( ref object currentRouteValues, string newValue)
{
if(!string.IsNullOrEmpty(newValue)){
// add a custom property here to currentRouteValues
// something like: (is wrong but I don't know how to proceed)
currentRouteValues = new { currentRouteValues, myCustoProperty = newValue };
}
}
How to do that automatically for above method ?
Thanks
I think this would answer your question.
Merging anonymous types
If you simply want to extract the data it would be something like this.
Object o = new { var1 = "the first var", var2 = "the second var" };
System.Type type = o.GetType();
foreach (var i in type.GetProperties())
{
Console.Write(i.GetValue(o));
}
But for merging, look at the link above.

Passing an array to Html.ActionLink()

I followed this example:
ASP.NET MVC - Pass array object as a route value within Html.ActionLink(...)
But, my Action is always called with null. What am I doing wrong?
foreach (OrderDetail od in order.OrderDetails)
{
rvd.Add("key" + count++, productID);
rvd.Add("key" + count++, productName);
}
#Html.ActionLink(linkText, "Renew", "Orders", rvd, new Dictionary<string, object>())
The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish..., but I get a null parameter in my Action below:
public ActionResult Renew(RouteValueDictionary rvd)
{
// 'rvd' is null here!
}
Please note: I don't know the number of parameters in advance.
The query string is correctly generated, like ?key0=dog&key1=cat&key2=fish...
No, this is not a correct url. A correct url would have looked like this:
?%5B0%5D.Key=123&%5B0%5D.Value=dog&%5B1%5D.Key=456&%5B1%5D.Value=cat...
which would have mapped to:
public ActionResult Renew(Dictionary<int, string> rvd)
{
...
}
You could write a custom ActionLink to generate this url:
public static class LinkExtensions
{
public static IHtmlString MyActionLink(
this HtmlHelper html,
string linkText,
string actionName,
string controllerName,
IDictionary<string, string> parameters
)
{
var a = new TagBuilder("a");
var urlHelper = new UrlHelper(html.ViewContext.RequestContext);
var query = string.Join("&", parameters.Select((x, i) => string.Format("[{0}].Key={1}&[{0}].Value={2}", i, urlHelper.Encode(x.Key), urlHelper.Encode(x.Value))));
var url = string.Format(
"{0}?{1}",
urlHelper.Action(actionName, controllerName, null, html.ViewContext.HttpContext.Request.Url.Scheme),
query
);
a.Attributes["href"] = url;
a.SetInnerText(linkText);
return new HtmlString(a.ToString());
}
}
which you could use like this in your view:
#Html.MyActionLink(
linkText,
"Renew",
"Orders",
order.OrderDetails.ToDictionary(x => x.ProductID.ToString(), x => x.ProductName)
)
You can read more about the correct wire format for binding to various collections in this blog post.
I imagine what is happening is you are expecting the model binder to bind your array to a RouteValueDictionary, but the model binder doesn't know that key0=dog&key1=cat&key2=fish is supposed to be a dictionary. I would recommend changing your code to accept a string array. To do this, your query string needs to look something like this: ?rvd=dog&rvd=cat&rvd=fish
And your Action...
public ActionResult Renew(string[] rvd)
{
// 'rvd' is no longer null here!
}
The important part is rvd is the parameter name in your action, as well as the name of each element in the querystring: ?rvd=dog&rvd=cat&rvd=fish. If you really want to use a dictionary instead of a string array, then your querystring should look like this: ?rvd[0]=dog&rvd[1]=cat&rvd[2]=fish, giving each item an array index, but you would probably have to change your parameter from RouteValueDictionary to Dictionary<string,string>, I'm not quite sure. More info here. EDIT: See Darin's comment about binding to a dictionary, as I believe his is correct.
You may have to write your own extension for Html.ActionLink that accepts an array (or whatever OrderDetails is) and creates the querystring as an array. This looks like a pretty good starting place.

Html.ActionLink with non-C# friendly htmlAttribute names

Disqus wants me to add an attribute called data-disqus-identifier to my links, but for obvious reasons, new { #data-disqus-identifier = "article" } gives me a syntax error.
Any idea what to do in these situations?
Thanks,
Rei
You can pass a Dictionary<string, object> with arbitrary string keys.
The syntax will be more verbose: new Dictionary<string, object> { { "data-disqus-identifier", "article" } }.
You may want to create an extension method or a static method on a static class with a short name that takes a smaller parameter set and returns the dictionary.
In MVC 3, if you use underscore _ instead of hyphens - in your property names then MVC will automatically convert them to hyphens in the resulting HTML. So, something like this should work for you:
new { data_disqus_identifier = "article" }

Select new object as a parameter while preserving it's run-time-generated type

Consider the following:
// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
// or create a new object
var subset = new {
Id = 1,
Name = "something random",
Email = "name#domain.tld"
};
Is there any way to use the subset variable as a parameter to a method, without it being cast as a plain Object? Can you somehow carry the auto-generated type of the variable?
I am trying to avoid having to create new classes every time I want to pass LINQ subsets to methods.
Random generic approaches are welcome.
No, passing anonymous types about isn't generally a good idea because you lose the type information*. You should create a concrete type and use that instead.
var subset = DataTable.Where(...).Select(row => new SomeType
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
Alternatively you can use the Tuple type if you are using .NET 4. This is a simple way to create "disposable" types and still get some type-safety.
*Actually there is a workaround, but I regard it is an ugly hack and would advise that you don't do it.
If I need to do this, I use resharper's "Replace Anonymous Type With Named Class" refactoring option. Then you have an appropriate named type to expose over the API, and you haven't had to do any work. This also gives you options to create it immutable (like anonymous types) or mutable, nested vs top-level, etc.
BTW, I don't recommend struct here (from the question).
Another option is to pass the behaviour into the method - i.e. an Action<int,string,string> callback - then do something like:
foreach(item in query) callback(item);
However, I don't like this as it is not obvious that there is a likely error in:
DoSomething(args, (id, email, name) => Email(To: email, Subject: name));
(the error being that it should probably be (id, name, email), if you see what I mean)
You can use a generic method:
public static void Foo<T>(T item)
{
// Do whatever
}
Then if you call
Foo(subset);
the compiler will infer T for you. Whether or not that actually helps you is another matter... it depends on what the method is meant to do. Obviously Foo can't refer to Id, Name, Email etc.
In general, if multiple methods should know about the same members, then you should use a named type. The usual case for passing them to generic methods is where the method really doesn't care about what type is involved, such as in LINQ.
I've made a feature request for C# 5 that we should be able to create types which have all the same features as anonymous types (immutability, equality, hash code generation, ToString dumping) but for simple named types. We'll see if it actually happens...
Anonymous Types don't provide much help outside of the context they where created.
If you need to pass an Anonymous Type to a method, either this method is very generic like (Example)
void PrintAllObjectProperties(object obj);
witch you would use reflection to do the work, or you are doing something wrong.
Here's what I came up with...
Extension method on Object:
public static class ObjectExtensions
{
/// <summary>
/// Cast Object to anonymous type.
/// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
/// </summary>
public static T ToAnonymousType<T>(this Object o, T t)
{
return (T)o;
}
}
Usage:
public void HandleAnonymousTypeAsParameter(Object o)
{
var anonymousType = o.ToAnonymousType(new
{
Id = new Int32(),
Foo = new String(),
Bar = new String()
});
// ... You can do this in even less characters:
var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}
HandleAnonymousTypeAsParameter(new
{
Id = 1,
Foo = "foo",
Bar = "bar"
});
Credits goes to John Skeet and Thomas P.

Categories

Resources