Scott Hanselman

T4MVC and R4MVC - Roslyn code generators for ASP.NET Core tag helpers

September 7, '17 Comments [21] Posted in ASP.NET MVC | Open Source
Sponsored By

I've always loved the T4 text generator within Visual Studio. If you are looking for T4 within Visual Studio 2017 you need to install the "Visual Studio extension development" option within the installer to access it. However, T4 development seems stalled/done and if you want to utilize some of it.

There's a nice open source project called T4MVC that you can use with Visual Studio 2015 and ASP.NET MVC to create strongly typed helpers that eliminate the use of literal strings in many places. That means instead of:

@Html.ActionLink("Dinner Details", "Details", "Dinners", new { id = Model.DinnerID }, null)

T4MVC lets you write

@Html.ActionLink("Dinner Details", MVC.Dinners.Details(Model.DinnerID))

Fast forward to 2017 and that team is working on a new project called R4MVC...it's a code generator that's based on Roslyn, the .NET Compiler Platform (hence the R).

It also lets you update your @Html.ActionLinks to be strongly typed, but more importantly it lets you extend that to strongly typed taghelpers, so instead of:

<a asp-action="Details" asp-controller="Dinners" asp-route-id="@Model.DinnerID">Dinner Details</a>

you can write

<a mvc-action="MVC.Dinners.Details(Model.DinnerID)">Dinner Details</a>

It's generating the URL for that <a> tag using the method and parameter.

Using an ASP.NET Core 1.1 app (2.0 is coming soon they say) I'll add the NuGet packages R4Mvc.Tools and R4Mvc, making sure to "include prerelease."

Adding R4Mvc.Tools in NuGet

I'll run "Generate-R4MVC" in the Package Manager Console.

Generate-R4MVC

There is a new R4Mvc.generated.cs file that gets created, and inside it is a whole bunch of classes based on the files on disk. For example I can type @Links.css, or @Links.lib and start getting intellisense for all my files on disk like JavaScript or CSS.

Links.css

When returning a view, rather than return View("About") I can do return View(Views.About):

return View(Views.About)

The R4MVC project also has Tag Helpers so their mvc-action attribute gives you strong typing like this:

<a mvc-action="MVC.Home.Index()">

This R4MVC project is just getting started, but I'm sure they'd appreciate your support! Head over to https://github.com/T4MVC/R4MVC/issues and learn about what they are planning and perhaps help out!

What do you think? Do you think there's value in smarter or strongly-typed URL generation with ASP.NET?


Sponsor: Raygun provides real time .NET error monitoring and supports all other major programming languages and frameworks too! Forget logs and support tickets. Reproduce software bugs in minutes with Raygun's error tracking software!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Thursday, 07 September 2017 19:21:50 UTC
Finally.

Magic strings are always a smell, and til now there was no decent way around it.

T4MVC was a pioneering project in that regard; it had some quirks and I never understood why Microsoft is not embracing it as a proper tooling feature of Visual Studio.
Thursday, 07 September 2017 20:44:17 UTC
Thanks for the nice review Scott !
I wanted to say that it already supports aspnetcore 2.0 !
Valeriob
Thursday, 07 September 2017 21:18:22 UTC
I also cannot comprehend why Microsoft doesn't promote/develop T4 further. It is an essential compliment to development in C#. Yet most developers have never even heard of it because its hidden in Visual Studio and the tooling for it hasn't been updated in 10 years.

It shouldn't require 3rd party extensions to get syntax highlighting with T4. VS 2017 should not ship with T4 debugging broken (https://stackoverflow.com/questions/43184337/cannot-debugging-t4-template-in-vs2017)

And yet for developers who have used it, T4 is extremely popular. This issue asking for better tooling had over 3,000 votes before it was tragically closed (https://visualstudio.uservoice.com/forums/121579-visual-studio-2015/suggestions/3793790-t4-editing).
Sam
Friday, 08 September 2017 00:15:19 UTC
Finally, We can get rid of the magic string. Good open source.
Friday, 08 September 2017 06:12:47 UTC
I also agree, that T4 is useful tool in C# developer's belt and I think there were two reasons, why it didn't get much traction are:
1) writing T4 code in Visual Studio without extensions is painful, third party extensions weren't stable (I've used ForTea extensions for R# (extension for extension :))
2) CodeDOM API was slow and hard to use, you could get away sometimes with reflection - but that had its own drawbacks (somewhat slow also, you needed to compile before transforming template and if template was generating C# code it was even more complicated ;)

So it became a closed loop - there's no critical mass in developer community to push Microsoft invest in T4 and there's no investments in T4 because there's no critical mass.
Giedrius
Friday, 08 September 2017 10:30:55 UTC
I believe the correct the approach for these kind of problems is either to use something like F#'s type providers or Nemerle's lisp style macros. Too bad c# doesn't support either. Generated code is still the code you have to maintain, and can be problematic on source control while merging.
Onur Gumus
Friday, 08 September 2017 17:07:53 UTC
Nice idea but when you are using Resharper seems quite useless. Resharper is able to analyze ASP.NET project and provide intellisense as well as static analysis for parameters which accept view's, controller's and actions's names.

Regarding Link TagHelper - providing controller's name, action's name and action's parameters as a separated attributes is very inconvenient and hard to maintain. It should be used with some helper with accepts Expression<Action<T>> as main parameter, something like that
<ns:a href="Link<HomeController>.ForAction(c=>c.Home(param))" text="Home" />

If you have Expression<Action<T>> you can extract from it many useful information (controller's name, action's name, action parameters) and generate url. Admittedly, this could have impact on application performance but gives you a very high level of maintainability.
Saturday, 09 September 2017 06:53:28 UTC
I really like this! I will have to see if I can integrate it into my site. I am currently doing something similar for the site I'm working on using Razor Pages. I wrote a quick blog detailing how I'm using it etc..

http://kurtdowswell.com/software-development/razor-pages-url-tt-file/
Monday, 11 September 2017 07:49:09 UTC
Hi Scott, very happy to see you covering this, it's amazing!

By the way, like Valeriob mentioned, we're already fully compatible with Asp.Net Core 2.0. The project is referencing 1.1 for backwards compatibility, but it's also tested in production on Asp.Net Core 2.0 with .NET Standard 2.0!
Artiom Chilaru
Monday, 11 September 2017 08:56:03 UTC
when you are using Resharper seems quite useless. Resharper is able to analyze ASP.NET project and provide intellisense as well as static analysis for parameters which accept view's, controller's and actions's names.

Sure, but R4Mvc isn't only used for action names, it goes further by also hard-typing the parameters that are passed to them. Furthermore, it ensures the links you created will link to the correct area, which you sometimes have to be careful with asp.net.

On top of that, it also ensures that the pages will work at run-time as well. Resharper is good and all, but it'll only provide develop time hints, and you can ignore/forget them. You could rename your action, and forget to update the view, and you'll know about it only when a user visits the page. T4MVC and R4Mvc ensure that you'll catch this error during build time (assuming you're building views as part of your test/deploy process)

Regarding Link TagHelper - providing controller's name, action's name and action's parameters as a separated attributes is very inconvenient and hard to maintain. It should be used with some helper with accepts Expression<Action<T>> as main parameter


Actually… :)

&lt;a asp-action="Details" asp-controller="Dinners" asp-route-id="@Model.DinnerID" &gt;Dinner Details&lt;/a&gt;


This is the standard ASP.NET Core MVC Tag Helper. That's what we're trying to get away from. R4MVC allows you to change the above to:

&lt;a a mvc-action="MVC.Dinners.Details(Model.DinnerID)" &gt;Dinner Details&lt;/a&gt;


The call to the action is considerably more readable, is strongly typed, the parameters are properly handled and validated. All this for practically no overhead at runtime. And, like you said, "gives you a very high level of maintainability."
Artiom Chilaru
Monday, 11 September 2017 08:57:08 UTC
P.S. This comments area should handle "pre" tags better :)
Artiom Chilaru
Tuesday, 12 September 2017 15:19:26 UTC
Thanks for the nice review you have shared.
Wednesday, 13 September 2017 14:22:38 UTC
Thanks for Great job, it's really informative post. I like aspnetcore 2.0!
192.168.0.1
Shanti
Wednesday, 13 September 2017 19:45:15 UTC
collection koimoi hungama
first day collection koimoi
Wednesday, 13 September 2017 19:45:41 UTC
1st day collection of koimoi hungama
collection koimoi hungama
Thursday, 14 September 2017 06:55:23 UTC
I got good ideas from this amazing blog. I am always searching like this type blog post. I hope I will see again.
Monday, 18 September 2017 12:00:01 UTC
wow good blog i like this blog thanx sir
Monday, 18 September 2017 12:01:31 UTC
Thanks for the nice review you have shared.
Monday, 18 September 2017 12:04:27 UTC
I got good ideas from this amazing blog. I am always searching like this type blog post. I hope I will see again. thanx
Wednesday, 20 September 2017 14:39:49 UTC
Nice project and amazing post, really interesting. Thank you.
vapovor
vapovor
Friday, 22 September 2017 20:36:21 UTC
Many thanks for the tips !!! They were valuable to me!
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.