6

I've noticed what I believe to be some odd behavior with T4MVC. Specifically, I'm attempting to build an ActionLink (using the HtmlHelper) for an action where the optional parameter value is null. This works fine most of the time. However, if the current route is of the same for which the ActionLink is being built AND the OptionalParameter has a non-null value, the resulting ActionLink will specify the value of the optional parameter from the current route context.

That's a wordy explanation, I think code will help clarify.

Controller

public virtual ActionResult Today(int? lineNumber = null)
{
    return Index(DateTime.Today, DateTime.Today, lineNumber);
}

Route

context.MapRoute(
    "TodaysProductionSchedules",
    "Production/{Controller}/Today/{lineNumber}",
    new
        {
            area = AreaName,
            controller = MVC.Production.ProductionSchedules.Name,
            action = MVC.Production.ProductionSchedules.ActionNames.Today,
            lineNumber = UrlParameter.Optional
        });

Razor

@Html.ActionLink("Show Today", MVC.Production.ProductionSchedules.Today(null))

As I mentioned earlier, if I am not currently on a view which is mapped to this route, the link will be generated correctly. However, if the current view does map the this route AND I either omit the value or supply null (as seen in the razor snippet), the lineNumber parameter will take its value from the current route value.

I think this might be a bug in T4MVC so I'll post a link to this topic on the T4MVC codeplex site as well. Thanks in advance!

Vinney Kelly
  • 4,975
  • 1
  • 25
  • 31
  • An obvious side-step is to map a route for the version of the url without the optional parameter but I don't consider this *the* answer. I've downloaded the source code; perhaps I'll get some time to see if I can't debug this error myself :) – Vinney Kelly Jul 20 '12 at 14:55

1 Answers1

3

Update 7/30/2012: This is fixed in T4MVC 2.10.1!

This was actually a recent regression from the model unbinder change. In t4mvc.tt around line 639, can you try changing AddRouteValues to the following:

    public static void AddRouteValues(RouteValueDictionary routeValueDictionary, string routeName, object routeValue) {
        IModelUnbinder unbinder;
        if (routeValue == null)
        {
            unbinder = DefaultModelUnbinder;
        }
        else
        {
            unbinder = ModelUnbinders.FindUnbinderFor(routeValue.GetType()) ?? DefaultModelUnbinder;
        }
        unbinder.UnbindModel(routeValueDictionary, routeName, routeValue);
    }

Original answer: I think generally in MVC, in many scenarios when a value is omitted from the new route, it gets its value from the current route, assuming that the high level values are the same (hence the two different cases you see).

So now the question is whether T4MVC can/should do something to avoid this behavior. I haven't checked the exact logic, but maybe if it always set this value in the route, that would disable this unwanted behavior.

But I think the first step is to fully understand the MVC behavior that's at play here before tackling the T4MVC case.

Feel free to take the investigation further and send a PR with the fix! :)

David Ebbo
  • 42,443
  • 8
  • 103
  • 117
  • You're right, David. It does appear to be an unintended consequence of the default behavior of the framework. Funny, I've been using MVC since v3 (and T4MVC for about the same amount of time) and somehow I managed to not have this issue but now I can't seem to avoid it. What really bothered me, however, is that eventhough I specified null as the parameter value, the route value was unchanged. Not sure (yet) if this due to T4MVC or MVC itself but this definately seems like something that T4 could (and probably should) handle. Do you agree? – Vinney Kelly Jul 23 '12 at 20:57
  • I agree the overall behavior is not ideal. Note that in your example, passing null or passing nothing at all produces the exact same IL, so the two cases couldn't behave differently. Going back to straight MVC, did you figure out what needed to be passed in the route values to get the behavior that you expect? – David Ebbo Jul 24 '12 at 09:53
  • Yes @david, the following will create the proper link: `@Html.ActionLink("Today 2", "Today", new { lineNumber = (int?)null })` This leads me to think that the issue could corrected through a slightly different handling of the parameterless T4MVC helper methods. – Vinney Kelly Jul 24 '12 at 19:42
  • Ah yes, I think I see the issue now. See update in my Answer. – David Ebbo Jul 31 '12 at 01:18
  • The fix in now in T4MVC 2.10.1 – David Ebbo Jul 31 '12 at 16:39
  • I noticed that your comment indicating that the fix is in the updated version of T4MVC doesn't get loaded in the inital set of comments. You might want to mention this in the accepted answer as well. – Vinney Kelly Aug 14 '12 at 22:00
  • it's the least I could do; your responsiveness and dedication to T4MVC are most excellent (as is the tool, I might add)! This library is one of the first NuGet packages I add to all MVC projects :) – Vinney Kelly Aug 16 '12 at 20:37
  • @DavidEbbo What if you want this functionality, I would like to infer route values from current context in my T4MVC, but in my current version it is not (3.0.2), I was going to ask a new question but this is the first hit in google results so probably good to append it to the answer here. – Paul Tyng Dec 04 '12 at 19:43
  • @PaulTyng maybe something can be done. Do you want to experiment with possible changes? – David Ebbo Dec 05 '12 at 00:48
  • @DavidEbbo I confirm this issue still exists on version 3.17.4 :( – Sadegh Aug 14 '16 at 18:58