T4MVC OptionalParameter values implied from current context
Asked Answered
S

1

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!

Spear answered 19/7, 2012 at 17:29 Comment(1)
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 :)Spear
C
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! :)

Ceram answered 22/7, 2012 at 20:52 Comment(10)
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?Spear
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?Ceram
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.Spear
Ah yes, I think I see the issue now. See update in my Answer.Ceram
The fix in now in T4MVC 2.10.1Ceram
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.Spear
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 :)Spear
@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.Adjure
@PaulTyng maybe something can be done. Do you want to experiment with possible changes?Ceram
@DavidEbbo I confirm this issue still exists on version 3.17.4 :(Leeanneleeboard

© 2022 - 2024 — McMap. All rights reserved.