Mvc: asp-all-route-data must be IDictionary<string,object> or RouteValueDictionary

Created on 27 Nov 2017  路  14Comments  路  Source: aspnet/Mvc

What if I need to pass values collection? For example:

<a href="@Url.Action(...., new { Values = new [] { "Foo", "Bar" } })"

Is this possible with asp-all-route-data?
I think it can be IDictionary<string,object> or RouteValueDictionary instead of IDictionary<string,string>.

I can't use anonymous class because keys must be with dot in my case. Example: FooFilter.Values.

Anonymous class declaration will produce compilation error:

<a href="@Url.Action(...., new { FooFilter.Values = new [] { "Foo", "Bar" } })"

and asp-all-route-data can't be used because it must be IDictionary<string,string>.

I found only one solution:

<a href="@Url.Action(...)?FooFilter.Values=Foo&FooFilter.Values=Bar"

and this solution is horrible....

question

All 14 comments

Is there a reason you don't use RouteValueDictionary then?

This Ok:

<a href="@Url.Action("List", "FooController", new RouteValueDictionary { {"area", "FooArea"}, {"FooFilter.Values", new[]{"StatusA", "StatusB"}} })">

But it can't be used for asp-all-route-data. And changing type of asp-all-route-data to IDictionary<string,object> couses problems with legacy code. So ASP-team will not agree to such changes I suppose...

If asp-all-route-data was IDictionary<string,object> then code will be more nice:

<a asp-action="FooAction" asp-route-status="@(new[]{ "StatusA", "StatusB" })"

the asp-route-data is an IDictionary because URIs have no concept of data types, so the question is if your not able to use anonymous data types (string:string) how to serialise/encode the values you have in such a way that they give you a IDictionary and some metadata? about the types; which of cause leads to the question of deserialisation by the receiving party into the correct type?

IMHO I think the real question that only you can answer is routing/uri the correct way to go about this ? or should the data be passed in a http post body that allows for transfer of structured/hyracical data? unlike URIs without custom encoding

https://en.m.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax

If you look at the implementaion of AnchorTagHelper then you will see that _routeValues is passing to RouteValueDictionary contructor. That's all. There is no special handling. And RouteValueDictionary is IDictionary<string,object>!

            RouteValueDictionary routeValues = null;
            if (_routeValues != null && _routeValues.Count > 0)
            {
                routeValues = new RouteValueDictionary(_routeValues);
            }

and then

tagBuilder = Generator.GenerateActionLink(.... routeValues: routeValues, ...);

From source:
https://github.com/aspnet/Mvc/blob/c27b07ef3f8b07dfe1d1de38a8d7bf2d6a9298f4/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs

@grahamehorner Why you say this is a problem for AnchorTagHelper.RouteValues but isn't for RouteValueDictionary? Problematic is one.

@Remleo yes your correct; however the RouteValueDictionary not just for creating URI links; and the boxing/unboxing the is performed at a low level by the appropriate urlHelper; by using a IDictionary for the AnchorTagHelper this prevents a non-string value from causing a problem deeper in the stack when generating the tags url

@grahamehorner In my view, the tag helpers are replacements of IUrlHelper's extensions. In Url.Action(...) I can use code like this: new { FooFilter = new [] { "Foo", "Bar" } }, but in asp-route-* I can't. And it is a bit confusing. This synthetic constraint is illogical as for me.

In my project I'm added own TagHelper with n-route-* property with type IDictionary<string,object> and yet no problems. In what cases it can cause problems?

@NTaylorMullen, are you aware of any scenarios, where the tag helper introduced by @Remleo can cause issues?

In what cases it can cause problems?

If you're looking to provide a string value for your route value you will end up having issues. You'll need to save your string value to a variable before setting it to the attribute.

This method is using asp-all-route-data:

@{
   IDictionary<string, string> routeData = new Dictionary<string, string>();
   routeData.Add("data1", "12345");
   routeData.Add("data2", "67890");
}
<a asp-controller="Home" asp-action="About" asp-all-route-data="routeData">Test Route Data</a>

Or in this way:

@{
   IDictionary<string, string> routeData = new Dictionary<string, string>
   {
      {"data1", "12345"},
      {"data2", "67890"}
   };
}
<li><a asp-controller="Home" asp-action="About" asp-all-route-data="routeData">Test Route Data</a></li>

You can also place the variable with "@" or without it in asp-all-route-data.
https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.taghelpers.anchortaghelper.routevalues

@MRT-77 have you read my first comment? Your example is too simple. What if I need to pass collection as value?

@NTaylorMullen Why do you see the problems in AnchorTagHelper.RouteValues but not in the RouteValueDictionary? AnchorTagHelper just uses RouteValueDictionary inside.

@NTaylorMullen Why do you see the problems in AnchorTagHelper.RouteValues but not in the RouteValueDictionary? AnchorTagHelper just uses RouteValueDictionary inside.

It's an artifact of how the TagHelper system tries to associate HTML with the AnchorTagHelper.RouteValues property. Going to deep dive a bit here but hopefully it helps understand the problem.

When you write:

@{
   IDictionary<string, string> routeData = new Dictionary<string, string>();
}
<a asp-controller="Home" asp-all-route-data="routeData">...</a>
<a asp-controller="Home" asp-route-foo="something">...</a>

Behind the scenes Razor generates something like:
```C#
IDictionary routeData = new Dictionary();
tagHelper = new AnchorTagHelper();
tagHelper.Controller = "Home";
tagHelper.RouteValues = routeData;
Write(tagHelper);

tagHelper = new AnchorTagHelper();
tagHelper.Controller = "Home";
tagHelper.RouteValues["foo"] = "something";


Razor does this because it has a special understanding of `Dictionary<string, TType>`. When a TagHelper has a `Dictionary<string, TType>` the system enables users to add single entries into that dictionary or set the entire thing (as shown by the example above). Now imagine if `RouteValues` was exposed as `Dictionary<string, object>`; in that world the TagHelper system allows users to provide anything other than strings. For example:

@{
var something = true;
}


would result in:
```C#
tagHelper = new MyTaghelperThatTakesStringObject();
tagHelper.Foo["key"] = something;

Note the lacking quotations around something. Basically, since HTML uses quotations to surround their attribute values but those quotations are also significant in a C# landscape we chose to have the route data represented as the most common case: a user doing string->string route key->values.

As a total side note, it would technically be possible to provide a attribute but that would require having two dictionaries and two sets of attributes on the TagHelper. Something we believe isn't a good user experience.

This seems to be a duplicate of closed issue #7826

We periodically close 'discussion' issues that have not been updated in a long period of time.

We apologize if this causes any inconvenience. We ask that if you are still encountering an issue, please log a new issue with updated information and we will investigate.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

NTaylorMullen picture NTaylorMullen  路  66Comments

angelsix picture angelsix  路  61Comments

mdmoura picture mdmoura  路  33Comments

dotnetjunkie picture dotnetjunkie  路  43Comments

MicahZoltu picture MicahZoltu  路  37Comments