Skip to content

Instantly share code, notes, and snippets.

@Eonasdan
Last active April 3, 2016 20:14
Show Gist options
  • Save Eonasdan/b3c3024b10e7da8d1fd7b262aac6a3f1 to your computer and use it in GitHub Desktop.
Save Eonasdan/b3c3024b10e7da8d1fd7b262aac6a3f1 to your computer and use it in GitHub Desktop.
Creates a strongly typed MVC urls: see http://vikutech.blogspot.com/2014/11/strong-typed-mvc-routing-on.html. Routing-Areas.tt is an enhancement to the original attempting to check if the route is in a Area. Routing-JsonResults.tt limits the generation to JsonResult Actions
<#@ template debug="true" hostSpecific="true" #>
<#@ assembly name="System.Core" #>
<#@ Assembly Name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".ts" #>
module Routing {
<#
// TODO: Give any controllers which need to be avoided.
var avoidClassNames = new List<string>{"AccountController"};
// TODO: Function names to avoid in code generation.
var avoidFunctionNames = new List<string>();
var env = (DTE)((IServiceProvider) Host).GetService(typeof(DTE));
var project = env.ActiveDocument.ProjectItem.ContainingProject;
foreach(var codeClass in GetAllCodeElementsOfType(project.CodeModel.CodeElements,vsCMElement.vsCMElementClass,false)
.OfType<CodeClass>().Where(cc => cc.IsDerivedFrom["System.Web.Mvc.Controller"] && !avoidClassNames.Contains(cc.Name) && !avoidClassNames.Contains(cc.FullName)))
{
var className = codeClass.Name.Replace("Controller","");
var area = "";
try
{
if (codeClass.FullName.Contains(".Areas."))
{
area = codeClass.FullName.Substring(codeClass.FullName.IndexOf(".Areas.", StringComparison.Ordinal) + 7);
area = "/" + area.Substring(0, area.IndexOf(".", StringComparison.Ordinal));
}
}
catch{}
#>
// Routing for <#= codeClass.FullName #>
export class <#=className #>Route {
<#
var allFunctions = GetAllCodeElementsOfType(codeClass.Members, vsCMElement.vsCMElementFunction, false);
foreach(var funcName in allFunctions.OfType<CodeFunction>().Where(fun => fun.Access == vsCMAccess.vsCMAccessPublic && fun.Name != codeClass.Name
&& !avoidFunctionNames.Contains(fun.Name)).Select(fun=>fun.Name).Distinct())
{
#>
static <#=funcName #> = '<#=area#>/<#=className #>/<#=funcName #>';
<#
}
#>
}
<#
}
#>
}
<#+
// https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude
// This function is from Tangible T4. All credit goes to them
public List<CodeElement> GetAllCodeElementsOfType(CodeElements elements, vsCMElement elementType, bool includeExternalTypes)
{
var ret = new List<CodeElement>();
foreach (CodeElement elem in elements)
{
if (elem.Kind == vsCMElement.vsCMElementNamespace)
{
ret.AddRange(GetAllCodeElementsOfType(((CodeNamespace)elem).Members, elementType, includeExternalTypes));
}
else if (elem.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal && !includeExternalTypes) continue;
else if (elem.IsCodeType)
{
ret.AddRange(GetAllCodeElementsOfType(((CodeType)elem).Members, elementType, includeExternalTypes));
}
if (elem.Kind == elementType) ret.Add(elem);
}
return ret;
}
#>
<#@ template debug="true" hostSpecific="true" #>
<#@ assembly name="System.Core" #>
<#@ Assembly Name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".ts" #>
module Routing {
<#
// TODO: Give any controllers which need to be avoided.
var avoidClassNames = new List<string>{"AccountController","ManageController","NationBuilder.MVC.Controllers.HomeController"};
// TODO: Function names to avoid in code generation.
var avoidFunctionNames = new List<string>();
var env = (DTE)((IServiceProvider) Host).GetService(typeof(DTE));
var project = env.ActiveDocument.ProjectItem.ContainingProject;
foreach(var codeClass in GetAllCodeElementsOfType(project.CodeModel.CodeElements,vsCMElement.vsCMElementClass,false)
.OfType<CodeClass>().Where(cc => cc.IsDerivedFrom["System.Web.Mvc.Controller"] && !avoidClassNames.Contains(cc.Name) && !avoidClassNames.Contains(cc.FullName)))
{
var className = codeClass.Name.Replace("Controller","");
var area = "";
try
{
if (codeClass.FullName.Contains(".Areas."))
{
area = codeClass.FullName.Substring(codeClass.FullName.IndexOf(".Areas.", StringComparison.Ordinal) + 7);
area = "/" + area.Substring(0, area.IndexOf(".", StringComparison.Ordinal));
}
}
catch{}
var allFunctions = GetAllCodeElementsOfType(codeClass.Members, vsCMElement.vsCMElementFunction, false).OfType<CodeFunction>();
if (!allFunctions.Any(x => x.Type.AsFullName.Contains("System.Web.Mvc.JsonResult"))) continue;
#>
// Routing for <#= codeClass.FullName #>
export class <#=className #>Route {
<#
foreach(var funcName in allFunctions.Where(fun => fun.Access == vsCMAccess.vsCMAccessPublic && fun.Name != codeClass.Name
&& !avoidFunctionNames.Contains(fun.Name) && fun.Type.AsFullName == "System.Web.Mvc.JsonResult").Select(fun=>fun.Name).Distinct())
{
#>
static <#=funcName #> = '<#=area#>/<#=className #>/<#=funcName #>';
<#
}
#>
}
<#
}
#>
}
<#+
// https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude
// This function is from Tangible T4. All credit goes to them
public List<CodeElement> GetAllCodeElementsOfType(CodeElements elements, vsCMElement elementType, bool includeExternalTypes)
{
var ret = new List<CodeElement>();
foreach (CodeElement elem in elements)
{
if (elem.Kind == vsCMElement.vsCMElementNamespace)
{
ret.AddRange(GetAllCodeElementsOfType(((CodeNamespace)elem).Members, elementType, includeExternalTypes));
}
else if (elem.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal && !includeExternalTypes) continue;
else if (elem.IsCodeType)
{
ret.AddRange(GetAllCodeElementsOfType(((CodeType)elem).Members, elementType, includeExternalTypes));
}
if (elem.Kind == elementType) ret.Add(elem);
}
return ret;
}
#>
<#@ template debug="true" hostSpecific="true" #>
<#@ assembly name="System.Core" #>
<#@ Assembly Name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".ts" #>
module Routing {
export declare var DomainName: string;
export function GetDomainName(): string {
return (DomainName != undefined && DomainName.length) ?
(DomainName.lastIndexOf('/') === -1 ? DomainName + '/' : DomainName) : '';
}
<#
// TODO: Give any controllers which need to be avoided.
var avoidClassNames = new List<string>{"AccountController"};
// TODO: Function names to avoid in code generation.
var avoidFunctionNames = new List<string>();
var env = (DTE)((IServiceProvider) Host).GetService(typeof(DTE));
var project = env.ActiveDocument.ProjectItem.ContainingProject;
foreach(var codeClass in GetAllCodeElementsOfType(project.CodeModel.CodeElements,vsCMElement.vsCMElementClass,false)
.OfType<CodeClass>().Where(cc => cc.IsDerivedFrom["System.Web.Mvc.Controller"] && !avoidClassNames.Contains(cc.Name) && !avoidClassNames.Contains(cc.FullName)))
{
var className = codeClass.Name.Substring(0, codeClass.Name.Length-10);
#>
// Routing for <#= codeClass.FullName #>
export class <#=className #>Route {
<#
var allFunctions = GetAllCodeElementsOfType(codeClass.Members, vsCMElement.vsCMElementFunction, false);
foreach(var funcName in allFunctions.OfType<CodeFunction>().Where(fun => fun.Access == vsCMAccess.vsCMAccessPublic && fun.Name != codeClass.Name
&& !avoidFunctionNames.Contains(fun.Name)).Select(fun=>fun.Name).Distinct())
{
#>
static <#=funcName #>() : string {
return GetDomainName() + '<#=className #>/' + '<#=funcName #>';
}
<#
}
#>
}
<#
}
#>
}
<#+
// https://github.com/PombeirP/T4Factories/blob/master/T4Factories.Testbed/CodeTemplates/VisualStudioAutomationHelper.ttinclude
// This function is from Tangible T4. All credit goes to them
public List<CodeElement> GetAllCodeElementsOfType(CodeElements elements, vsCMElement elementType, bool includeExternalTypes)
{
var ret = new List<CodeElement>();
foreach (CodeElement elem in elements)
{
if (elem.Kind == vsCMElement.vsCMElementNamespace)
{
ret.AddRange(GetAllCodeElementsOfType(((CodeNamespace)elem).Members, elementType, includeExternalTypes));
}
else if (elem.InfoLocation == vsCMInfoLocation.vsCMInfoLocationExternal && !includeExternalTypes) continue;
else if (elem.IsCodeType)
{
ret.AddRange(GetAllCodeElementsOfType(((CodeType)elem).Members, elementType, includeExternalTypes));
}
if (elem.Kind == elementType) ret.Add(elem);
}
return ret;
}
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment