Dotnet-Boxed / Templates

.NET project templates with batteries included, providing the minimum amount of code required to get you going faster.
https://RehanSaeed.com
MIT License
3.41k stars 492 forks source link

Auto Generation of Constants #66

Closed mcquiggd closed 8 years ago

mcquiggd commented 8 years ago

@RehanSaeed

If you are interested, I have created T4 Templates to automatically generate the Constants for:

I am currently using VS 2015 Update 1 with the ASP.Net 5 MVC6 version. I see no reason why they would not work with other combinations. They automatically update on Build by default.

If you are interested, let me know and I will add a sample on GitHub to see what you think. You might notice I have used a slightly different naming convention from the base template, as there are a few areas where things are not quite fitting.

I would be interested in contributing to the project.

Examples:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool 28/01/2016 00:06:42 UTC.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Pro.Website.Constants
{
    public static class ActionNames
    {
        public static class ErrorController
        {
            public static class Get
            {
                public const string Error = "Error";
            }
        }
        public static class HomeController
        {
            public static class Get
            {
                public const string About = "About";
                public const string Contact = "Contact";
                public const string Index = "Index";
            }
            public static class Route
            {
                public const string BrowserConfigXml = "BrowserConfigXml";
                public const string Feed = "Feed";
                public const string ManifestJson = "ManifestJson";
                public const string OpenSearchXml = "OpenSearchXml";
                public const string RobotsText = "RobotsText";
                public const string Search = "Search";
                public const string SitemapXml = "SitemapXml";
            }
        }
    }
}
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool 30/01/2016 00:09:24 UTC.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Pro.Website.Constants
{
    public static class ViewNames
    {
        public static class ErrorController
        {
            public const string Error= "Error";
        }
        public static class HomeController
        {
            public const string About= "About";
            public const string Contact= "Contact";
            public const string Index= "Index";
        }
        public static class SharedController
        {
            public const string _Footer00= "_Footer00";
            public const string _Header00= "_Header00";
            public const string _Header01= "_Header01";
            public const string _Header02= "_Header02";
            public const string _Header03= "_Header03";
            public const string _Header04= "_Header04";
            public const string _Layout= "_Layout";
        }
    }
}
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool 28/01/2016 00:08:08 UTC.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Pro.Website.Constants
{
    public static class ControllerNames
    {
        public const string Error = "Error";
        public const string Home = "Home";
    }
}

Which can then be called as follows:

<a asp-controller="@ControllerNames.Home" asp-action="@ActionNames.HomeController.Get.Contact">Contact Us</a>`
[Route("browserconfig.xml", Name = ActionNames.HomeController.Route.BrowserConfigXml)]`
<link href="@Url.RouteUrl(ActionNames.HomeController.Route.BrowserConfigXml)"
[HttpGet("contact", Name = ActionNames.HomeController.Get.Contact)]
public IActionResult Contact()
{
    return View(ViewNames.HomeController.Contact);
}
RehanSaeed commented 8 years ago

This is pretty interesting. All pull requests are welcome.

Would you see this as an option selected by the user or turned on by default? The Route constants you have created are all GET's so they can be added in the Get static class. Does this play nice with ASP.NET 5 projects? Do you have a sample project that I can try out? Some people prefix their method names with the HTTP verb e.g. GetHome, how would that affect this?

mcquiggd commented 8 years ago

Hi @RehanSaeed

comments inline:

Would you see this as an option selected by the user or turned on by default?

These T4 templates are part of a set of architecture building blocks I use, and I did not want to use T4MVC as it is simply too heavy for my needs. My preference is to break the functionality of my architectures into NuGet packages; my intention was to create a NuGet package with a configuration file that allows customisation of the output. It could be used standalone, but also fits very nicely with MVC Boilerplate. If you like the solution, you could add that to the existing wizard for MVC Boilerplate as a package reference, and / or people can simply add it later. I think that has the most flexibility.

The Route constants you have created are all GET's so they can be added in the Get static class.

Actually that is only because there were no HTTPPost Actions in the sample project I was using. You would see the following if I included HTTPPost (and I intend to support all HTTP Verbs e.g. for full REST APIs).

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool 28/01/2016 00:06:42 UTC.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Pro.Website.Constants
{
    public static class ActionNames
    {
        public static class ErrorController
        {
            public static class Get
            {
                public const string Error = "Error";
            }
        }
        public static class HomeController
        {
            public static class Get
            {
                public const string About = "About";
                public const string Contact = "Contact";
                public const string Index = "Index";
                public const string Register = "GetRegister";

            }
            public static class Post
            {
                public const string Register= "PostRegister";
            }
            public static class Route
            {
                public const string BrowserConfigXml = "BrowserConfigXml";
                public const string Feed = "Feed";
                public const string ManifestJson = "ManifestJson";
                public const string OpenSearchXml = "OpenSearchXml";
                public const string RobotsText = "RobotsText";
                public const string Search = "Search";
                public const string SitemapXml = "SitemapXml";
            }
        }
    }
}

If no HTTP Verb is specified, I default to placing them in a static class named 'Route'.

Does this play nice with ASP.NET 5 projects?

Yes, I am using it with VS 2015 Update 1 with the latest ASP.Net 5 MVC6 version of MVC Boilerplate as I mentioned above.

Do you have a sample project that I can try out?

Not yet, but I will shortly be placing this on GitHub, with a sample based on ASP.Net 5 MVC6 version of MVC Boilerplate.

Some people prefix their method names with the HTTP verb e.g. GetHome, how would that affect this?

It would not have any impact. The T4 Templates examine any attributes such as HTTPGet, HTTPost, and that determines which static class the constants are placed in. They examine the name of the Action method, to determine the name of the constant.

These Route name constants are names that identify the Routes that are added into the Route table, and simply have to be unique and easy to comprehend - the template portion of the attribute is what is used for Route matching in a requested URL; the Route name constant is used by the Tag Helpers to make sure you provide that correct URL - it just keeps it all synchronised.

The reason I took this approach in terms of structure and naming convention, is sometimes people have for example, a Controller named Error and an Action named Error, which is not allowed in the parent child static class structure used for the Constants, as you cannot have a member with the same name as its enclosing type.

So, in order to keep a consistent naming convention, currently my defaults are the following:

Actions - scan the namespace specified for Controllers (defaults to ProjectName.Controllers)

ActionNames.[Controller].[HTTPVerb].[ActionName]

E.G ActionNames.HomeController.Get.Index ActionNames.AccountController.Get.Register ActionNames.AccountController.Post.Register or ActionNames.AccountController.Post.RegisterPost ActionNames.HomeController.Route.BrowserConfigXml

Views - scan the Views Folder and build constants based on the following:

ViewNames.[Folder]Controller.[ViewName]

E.G. ViewNames.HomeController.Contact ViewNames.SharedController._Header

I am adding a config.ttinclude file to enable a number of settings to be overridden.

Regards,

David

RehanSaeed commented 8 years ago

Looks interesting. Would like to have a play when you post it to GitHub. BTW, does T4 work with Visual Studio Code? I don't think many people use it yet for ASP.NET development but it looks to be getting more popular.

mcquiggd commented 8 years ago

Re: T4 and Code - I honestly don't know - I don't currently have a problem for which Visual Studio Code would be a solution. I will look into it later - I am pretty busy today.

In fact, the ASP.Net team were originally not even going to support T4 in ASP.Net 5, until there were quite a few people complaining about that, so they implemented support for Single File Generators, so T4 does work in ASP.Net 5.

There was some discussion about a 'future approach' to Code Generation, and it seems the current preference is for it to be based on Razor Syntax. But there really is not a coherent story yet, as it kind of got lost in the rush to make Asp.Net 5 cross platform.

But it is a subject that will be addressed. It's all become a bit messy, as T4 is used by other teams, such as Entity Framework etc.

David Ebbo's Razor Generator has a 'template option', but standard T4 is just simpler.

At the moment, people are doing some things with Yeoman, but this is more project scaffolding than code generation based on meta data - please, if anyone reading this knows better, please correct me.

Roslyn is capable of code generation, but that is not really its main focus, and it is very verbose for doing even simple code creation. It could be great for extracting meta data though.

So, at the moment I have stuck to T4 and CodeDom. T4 is still by far the most flexible Code Generation engine available to .Net people, able to output any file type, work with meta data from a database, file system, HTML etc.

If a cross platform Code Generation strategy does coalesce, then I will port my approach to that. But I am focused on the main use case of Visual Studio on Windows.

mcquiggd commented 8 years ago

Hi @RehanSaeed

I have uploaded an initial sample of automatically generating Controller Names, Action Names and View Names with both the MVC5 and MVC6 versions of MVC Boiler Plate.

https://github.com/Pro-Coded/MVCMagic

The main place to look is

https://github.com/Pro-Coded/MVCMagic/tree/master/MVCMagicSampleMVC6BP/Constants

Here you will see two things:

Firstly that I have deleted the folders for HomeController and ErrorController, and renamed the other three classes to be pluralised (e.g. EnvironmentName becomes EnvironmentNames to match my preferred naming convention that also avoids a collision with a .Net type).

Secondly, there is a folder named MVCMagic that contains two .ttinclude files, and three .tt files. These are pretty self explanatory; the file "_MVCMagicConfiguration.ttinclude" contains a few options to change naming used for the output.

I have then changed the Controllers and Views to use the autogenerated constants. There are no additional packages or dependencies compared to an original MVC Boiler Plate Project.

I'll be adding some more features over the next couple of days; let me know what you think... I want some feedback to see if there is an audience for this before releasing a NuGet.

David

RehanSaeed commented 8 years ago

Hi David,

I've had a fairly detailed look at the code on GitHub, haven't had a chance to run it yet as I've been short on spare time over the last couple of months. I'm very impressed with it, I particularly like the static file constants. Some quick feedback based on what I've seen so far:

There is a hosting.json file where the wwwroot folder name setting is specified, you should parse that to get the name of that folder and default to wwwroot if that .json file does not exist (The gulpfile.js does the same thing).

I will want to try Visual Studio Code to see if it supports T4 but I suspect it will not. However, this feature is pretty cool so I'm thinking this could be an optional feature so the VS Code devs can still use it. Probably experimental in the beginning while it beds in like the .cshtml minification I added.

mcquiggd commented 8 years ago

Thanks!

Yes, I am working on allowing a set of paths to be specified for Content File locations, and one of the many things to bear in mind is the difference between MVC5, where the Project root corresponds to "~", and there are typically Content, Scripts etc. directories, and MVC6 with the concept of wwwroot, which corresponds to "~", and as you mention, can be renamed (or in extreme cases, not even used).

As part of working with the json files, I will also be looking to strongly type the configuration.

So, I am close to a solution that works for both; today Carnaval starts here in Brasil but I am keen to get an initial release with the first functionality ASAP. I will continue to evolve it for my own internal use, and will share with the community via GitHub.

I mentioned what I was working on, on the ASP.Net thread https://github.com/aspnet/Home/issues/272, and maybe @sayedihashimi will see this as an example of how flexible and varied the use of Code Generation with T4 can be, compared to say Yeoman.

There are lots of tools for creating project templates, but with T4 we are really talking about something with a much wider scope, from working with metada from, and producing, code, HTML, databases, SQL, JavaScript, XAML etc; this kind of capability was / is / could be a huge productivity win for .Net, and it is a real pity it has not yet been fully exploited by Microsoft.

As I said, I am looking to keep this flexible to support any new, official cross-platform tool that offers the same capabilities as T4 emerges. Incidentally, T4 has been available in SharpDevelop, MonoDevelop (and I believe Xamarin Studio), and has been unofficially ported to CoreCLR within Microsoft.

So, essentially it is already running on Windows, OSX, Linux, can already work with Roslyn etc.

Personally the main change I would make to it's core is to be able to use Razor syntax, and then proper tooling support (syntax highlighting, refactoring), perhaps in a similar fashion to the OmniSharp project.

What we are really missing is a clear direction from Microsoft.

Anyway, sorry for droning on :-)

Future interesting projects include making use of the new Localisation / Globalisation in Asp.Net 5 which I would be happy to collaborate on, and I will be looking to include such features as route localisation etc, work with SPAs (e.g. Aurelia).

RehanSaeed commented 8 years ago

I remember reading that issue a while back, so you were the one driving it's development! I agree about the clear direction. It's support in VS Code would go a long way for this as .NET Core is now supposed to be cross platform. I've been meaning to look into Angular 2 and Aurelia, too much to learn, so little time...

mcquiggd commented 8 years ago

He he, yes, I was 'making a nuisance' of myself over at ASP.Net regarding T4 ;-)

I am very much into Code Generation; years ago I contributed to a framework for .Net using CodeSmith. Code Generation is a huge productivity boost.

(I updated my comment a little while you were posting... and by the way do you prefer to be called Muhammad?)

David

RehanSaeed commented 8 years ago

I have always meant to look into T4, it seems like kind of a dark art to me. The only place I remember Microsoft using it is with EntityFramework and it was pretty useful.

Something I hadn't realized was that every country in the world has a different name order (First, Middle, Last), there is a really cool map of it somewhere on the internet. It just so happens that my first name that most people call me by is Rehan.

mcquiggd commented 8 years ago

Entity Framework... and also T4 is what was used for MVC templates, also in some SSDT packages and a lot of open source architecture frameworks.

And I am actually British, with an Irish surname, but live in Brasil now.. my wife is Brasilian, and we decided to settle here... I am setting up a software company here, and trying not to be distracted by the beach life and parties :-)

Nice to meet you Rehan... and my compliments on your work.

David Paul McQuiggin

RehanSaeed commented 8 years ago

Ah, here I was thinking I was talking to a Brazilian when I in fact you are a fellow countryman!

sayedihashimi commented 8 years ago

See https://github.com/aspnet/Tooling/issues/394

As I said, I am looking to keep this flexible to support any new, official cross-platform tool that offers the same capabilities as T4 emerges. Incidentally, T4 has been available in SharpDevelop, MonoDevelop (and I believe Xamarin Studio), and has been unofficially ported to CoreCLR within Microsoft.

I'm not aware of any efforts to get T4 working cross platform. That doesn't mean that it's not happening. If you have some link with more info please share that.

Also somewhat related see https://github.com/ligershark/side-waffle/issues/324#issuecomment-180166326.

Regarding names, outside of the professional world I go by my middle name "Ibrahim" because it's a family/religious tradition. All males in my family are named "Sayed Hashimi". See http://www.ancestry.com/name-origin?surname=sayed

mcquiggd commented 8 years ago

I'm definitely an English man - can't dance, and have to wear factor 100 sun block ;-)

@sayedihashimi Nice to see you here Ibrahim... and great to see you have opened https://github.com/aspnet/Tooling/issues/394

Already commented - you know I cannot resist ;-)

I mention the Mono TextTemplating port. https://github.com/mono/monodevelop/tree/master/main/src/addins/TextTemplating

Which is published as a NuGet, listing copyright as Xamarin. https://www.nuget.org/packages/Mono.TextTemplating/

RehanSaeed commented 8 years ago

@sayedihashimi Are there any plans to get T4 working in VS Code? Who do we need to speak to about it? @mcquiggd has built something very cool which solves the problem of having to use strings in ASP.NET Core for controller names, action names, view names and even static file paths. I would like to include it by default in Boilerplate but only if it supports all platforms. BTW, I've seen people refer to you as Sayed on Chennal 9, I've done the same and let people call me Muhammad sometimes when I get tired of explaining the name situation.

@mcquiggd I tried out your project today, I couldn't get the files to auto generate on project build. I had to use the 'Run Custom Tool' context menu item. BTW, you are missing some lovely grey skies and drizzling rain for the past two weeks.

mcquiggd commented 8 years ago

Ah, that is another missing feature in vanilla Visual Studio (strangely VS2015 does have one setting for T4 to 'show warning before running' - there are a number of Extensions to make up for this, such as:

AutoT4 https://visualstudiogallery.msdn.microsoft.com/84e6f033-6da3-4641-a058-12feef0a33b9

After installing, go to Tools > Options > AutoT4, and select 'After Build'.

Productivity Power Tools, from Microsoft, also offers 'Transform Templates' at the Project Level, but not Solution Level (right click on a Project and select Power Commands). https://visualstudiogallery.msdn.microsoft.com/34ebc6a2-2777-421d-8914-e29c1dfa7f5d

Part of me thinks it's a shame we need to add an extension for something such as this, but on the other hand, maybe it is the best way to deliver features that can be updated, to the core Visual Studio? I am in two minds about that. I am trying to keep dependencies to a minimum on my T4 projects.

@sayedihashimi Ibrahim, do you also know a contact on the Visual Studio and Visual Studio Code Teams that can be involved in your discussion regarding Single File Generators?

mcquiggd commented 8 years ago

@RehanSaeed @sayedihashimi

And not forgetting what was mentioned on the original thread at https://github.com/aspnet/Home/issues/272

There is also a project here by a member of the Entity Framework team and updated to RC1 here (it was this project that was mentioned on the thread by its maintainer Tibor @totht91)

That would indicate it would be possible to incorporate T4 into an Asp.Net 5 pipeline via Gulp,and a command, working cross platform in VS Code, or indeed anything compatible with the dnx.

It also supports Razor syntax.

I will try to create a sample for MVCMagic using this approach. But, bear in mind, to make the templates usable cross platform, I need to convert the Visual Studio specific meta data extraction code to use Roslyn (something I am already working on).

RehanSaeed commented 8 years ago

@mcquiggd I want to implement this feature but the ASP.Net Core tooling all seems to be in a state of flux at the moment. I'm going to wait and see what happens with after RTM which is not too far away.

Being a bit obsessive about keeping a clean issue list, I'm going to close this issue for now but if you, @sayedihashimi or I spot anything new, go ahead and re-open this issue. The ideal situation would be cross-platform T4 support but we'll wait and see.

sayedihashimi commented 8 years ago

I'm having a hard time following this thread, maybe we can meet on skype to discuss if you're having issues that I can help with.

FYI I'm not aware of any efforts to make T4 work cross platform.

mcquiggd commented 8 years ago

@RehanSaeed , @sayedihashimi

Hi both,

from my side, I had progressed to VS Code support using a command line and gulp command, based on this project that I had mentioned before:

https://github.com/totht91/TextTemplating

That handles cross-platform T4 pretty well, and I had made a couple of changes to allow a quick way to import custom assemblies into its Razor syntax (.cshtml file) version. I ported a sample of my original T4 code to Roslyn, and an initial test T4, and also a Razor syntax version of one of my original templates could be successfully run using VS Code and command line on Windows and Linux.

I was then kind of in limbo on this due to the push back on the release date and new command line work of ASP .Net Core etc, and as what I had already works with MVC Boilerplate etc. in Visual Studio, I left that project on the shelf to move onto a DocumentDB implementation of a CQRS framework.

For me this is not dead, but I do certainly agree with closing this thread.

When I have time I will update the initial project I placed on GitHub with the code I had shelved, and let you both know when that is done, so if you find time, you can see if it is of interest / a starting point for something more polished, and then I would be more than happy to Skype...

By the way, keep up the good work both of you... :-)

Cheers!

David

tibitoth commented 8 years ago

@mcquiggd I'm glad to see my TextTemplating project is useful :) Please feel free to contribute to my repo. I will update to RC2 in June, and release a nuget package.

mcquiggd commented 8 years ago

@totht91

Tibor - more than happy to!

Will be in touch.

David