T4MVC 2.6.10: fluent route value API, shorter way to refer to action, and more
To get the latest build of T4MVC:
Go to T4MVC page on CodePlex
I just posted build 2.6.10. There were also a few builds in between since I last blogged about 2.6, so this post describes some of those changes (full history here).
Fluent route value API
As you probably know, T4MVC uses a pattern where the route values are encapsulated using a pseudo-call to a controller action, e.g.
Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID))
This adds the controller, the action and the method parameters to the route values in a convenient way with no hard coded strings.
But in some situation, you may need to add extra values to the route that don’t exist in the action method. Now you can do it as follows:
Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
.AddRouteValue("foo", 17))
You can add multiple values this way using a fluent approach, e.g.
Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
.AddRouteValue("foo", 17)
.AddRouteValue("bar", "abc"))
As an alternative for adding multiple values, you can write:
Html.ActionLink("Delete Dinner", MVC.Dinners.Delete(Model.DinnerID)
.AddRouteValues(new { foo = 17, bar = "abc"}))
Note that in some cases, you may want to omit the action method parameters from the route, possibly because they are meant to come from form data and not from the URL. For those cases, you can always use the parameter-less override that T4MVC generates (that part is not new), e.g.
Html.ActionLink("Delete Dinner", MVC.Dinners.Delete()
.AddRouteValues(new { foo = 17, bar = "abc"}))
The nice thing about this fluent approach is that it doesn’t require adding overloads to all the T4MVC helpers (e.g. Html.ActionLink is this case) to take in the extra route values. Instead, it automatically applies to any method that uses the T4MVC pattern.
Shortcut to refer to controller actions from within the controller
Suppose you want to redirect to a different action in the same controller. Normally, in MVC you write something like:
return RedirectToAction("Details", new { id = dinner.DinnerID });
With T4MVC, you’ve always been able to write:
return RedirectToAction(MVC.Dinners.Details(dinner.DinnerID));
And now you can simplify it a bit and write:
return RedirectToAction(Actions.Details(dinner.DinnerID));
So basically, ‘Actions’ is equivalent to MVC.Dinners when you’re within the Dinners controller.
Now some might be tempted to take it a step further and just write:
return RedirectToAction(Details(dinner.DinnerID));
But that would be horribly wrong, since you would end up actually calling your controller action directly, which is not what you want! We’re only trying to make a pseudo call to it that captures the route values.
Small breaking change: Actions renamed to ActionNames
This is a result of the previous change. Unfortunately, the token ‘Actions’ was already used as the way to get all the action names for a controller. So I ended up renaming it to ActionNames, since Actions was the best name for the new feature above.
So if you’re currently using the pattern ‘MVC.Dinners.Action.Delete’ (to refer to the action name “Delete”), you’ll need to change it to MVC.Dinners.ActionNames.Delete.
Sorry for the little extra pain!
Generate full view paths to allow cross controller references
Previously, when you wrote something like MVC.Dinners.Views.Edit, it would evaluate to “Edit”. This works fine as long as you are using it from the Dinners controller, which implies that this is the Dinners Edit view as opposed to another controller’s Edit view.
But in some cases, you may want to refer to another controller’s view, and just having the short name no longer works. So T4MVC was changed to instead make MVC.Dinners.Views.Edit evaluate to the full "~/Views/Dinners/Edit.aspx", which removes all ambiguities.
As far as I know, it should not affect any scenarios that use it, as it never hurts to be more specific. But let me know if you run into something…
Added way to get area name from both Area and Controller objects
T4MVC now makes the area name available, either from the Area and the Controller. e.g. you can write MVC.MyArea.Name or MVC.MyArea.MyController.Area, both of which will evaluate to the area name (here “MyArea”).
Comments
Anonymous
January 04, 2010
Good work, loving the fluent syntax.Anonymous
January 04, 2010
It's great that you changed to full url for views. That should boost performance a bit. See for example: http://blog.dynatrace.com/2009/04/22/aspnet-mvc-hidden-performance-problem-with-htmlhelperrenderpartial-functions/Anonymous
January 05, 2010
@Mikael: good point. I didn't have perf in mind with this change, but it does make sense that it takes less work if the path is already full.Anonymous
January 24, 2010
Great work! I think I found a small bug, It's true that the code will modify all the Controller class to partial on the the first run. But if the controller got modified again back to normal because of other mean such as code gen. T4MVC will not change it back to partial again (unless I deleted all the class generated by T4MVC)Anonymous
January 24, 2010
Aiya I think I found another bug... must be my lucky day. If there is two parent/child folder with the same name under the Content folders... (or under view or controller I would imagine). Then we'll get an error member names cannot be the same as their enclosing type. This is because T4MVC use the folder name as the class name. So a nested class with the same name is a no go. I admit this is a very rare case but it's a bug nevertheless.Anonymous
January 24, 2010
The comment has been removedAnonymous
January 30, 2010
David, For the first issue, I invoked the transform all template and that took care of it. Re-saving it probably have the same effect. Thanks again for your hard work.