我已经能够使用血液、汗水、眼泪和 Tangible T4 的 TemplateFileManagerV2.1.ttinclude 及其 VisualStudioAutomationHelper.ttinclude 解决我的问题,尽管在以下帖子中通过 Tangible T4 支持建议的修改:
Tangible T4 support advice for editing their Visual Studio Automation Helper to allow creating files that are not wrapped in a .txt4 file
因为我没有有形 T4 的专业版,所以有点痛苦。嘿嗬,我不是在嘴里寻找礼物马。
唯一突出的问题是我无法检测源文件中的属性是否是虚拟的,所以我也在我的伙伴元数据类中获得了导航属性,这是我不想要的。我会活到另一天与之抗争。
另外,我可以创建文件,但它们不包含在项目中。包含它们的代码很简单,但我无法让它在同一个文件中工作,因此必须将其拆分为单独的文件,如下所示:
T4_1_GenerateCodeFirstBuddies.tt
T4_2_GenerateCodeFirstBuddies.tt
这种分离具有附带的好处,因为 T4_1_GenerateCodeFirstBuddies.tt 使用两个有形 T4 帮助程序 .ttinclude,其中一个会留下残留错误。运行我的第二个文件会删除解决方案资源管理器中的错误和红色波浪线,我觉得这真的很让人分心。
所以,我的文件的代码如下:
T4_1_GenerateCodeFirstBuddies.tt
<#@ template debug="true" hostSpecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="EnvDTE" #>
<#@ include file="VisualStudioAutomationHelper.ttinclude" #>
<#@ include file="TemplateFileManagerV2.1.ttinclude" #><#
var modelFileDirectory = this.Host.ResolvePath("Models");
var metaDataFilesDirectory = this.Host.ResolvePath("MetaData");
var nspace = "";
var manager = TemplateFileManager.Create(this);
foreach(var file in System.IO.Directory.GetFiles(modelFileDirectory, "*.cs"))
{
var projectItem = this.VisualStudioHelper.FindProjectItem(file);
foreach(EnvDTE.CodeClass classInFile in this.VisualStudioHelper.CodeModel.GetAllCodeElementsOfType(projectItem.FileCodeModel.CodeElements, EnvDTE.vsCMElement.vsCMElementClass, false))
{
var name = classInFile.Name;
if(nspace == "") nspace = classInFile.Namespace.Name;
// Danger: Beware if a table name includes the string "Context" or "AspNet"!!
// These files are removed because they are either the DbContext, or the sysdiagram file, or else the AspNet.Identity tables
if(name != "sysdiagram" && name.IndexOf("Context") == -1 && name.IndexOf("AspNet") == -1)
{
if(!FileExists(metaDataFilesDirectory, classInFile.Name + "MetaData.cs"))
{
manager.StartNewFile(name +"MetaData.cs", "", "MetaData"); #>
using System;
using System.Collections.Generic;
using System.ComponentModel;
//using System.ComponentModel.DataAnnotations;
//using Wingspan.Web.Mvc.Extensions;
using Wingspan.Web.Mvc.Crud;
namespace <#= nspace #>
{
public class <#= name + "MetaData" #>
{
<# foreach (CodeElement mem in classInFile.Members)
{
if (mem.Kind == vsCMElement.vsCMElementProperty) // && "[condition to show that mem is not marked as virtual]")
{
PushIndent(" ");
WriteLineDisplayName(mem);
WriteLineProperty(mem);
WriteLine("");
PopIndent();
}
} #>
}
public partial class <#= name #> : IInjectItemSL
{
public ItemSL ItemSL
{
get
{
return new ItemSL
{
ItemId = <#= name #>Id, ItemText = Name
};
}
}
}
}<#
}
}
}
}
manager.Process();
#>
<#+
// Check for file existence
bool FileExists(string directory, string filename)
{
return File.Exists(Path.Combine(directory, filename));
}
// Get current folder directory
string GetCurrentDirectory()
{
return System.IO.Path.GetDirectoryName(Host.TemplateFile);
}
string GetRootDirectory()
{
return this.Host.ResolvePath("");
}
// Get content of file name
string xOutputFile(string filename)
{
using(StreamReader sr =
new StreamReader(Path.Combine(GetCurrentDirectory(),filename)))
{
return sr.ReadToEnd();
}
}
// Get friendly name for property names
string GetFriendlyName(string value)
{
return Regex.Replace(value,
"([A-Z]+)", " $1",
RegexOptions.Compiled).Trim();
}
void WriteLineProperty(CodeElement ce)
{
var access = ((CodeProperty) ce).Access == vsCMAccess.vsCMAccessPublic ? "public" : "";
WriteLine(access + " " + (((CodeProperty) ce).Type).AsFullName + " " + ce.Name + " { get; set; }");
}
void WriteLineDisplayName(CodeElement ce)
{
var name = ce.Name;
if (!string.IsNullOrEmpty(name))
{
name = GetFriendlyName(name);
WriteLine(string.Format("[DisplayName(\"{0}\")]", name));
}
}
#>
T4_2_GenerateCodeFirstBuddies.tt:
<#@ template debug="true" hostSpecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="EnvDTE" #>
<#@ include file="VisualStudioAutomationHelper.ttinclude" #>
<#@ include file="TemplateFileManagerV2.1.ttinclude" #><#
var metaDataFilesDirectory = this.Host.ResolvePath("MetaData");
var metaDataFiles = System.IO.Directory.GetFiles(metaDataFilesDirectory, "*.cs");
var project = VisualStudioHelper.CurrentProject;
var projectItems = project.ProjectItems;
foreach( var f in metaDataFiles)
{
projectItems.AddFromFile(f);
}
#>
生成的输出文件对我来说已经足够好了,看看以下几行:
using System;
using System.Collections.Generic;
using System.ComponentModel;
//using System.ComponentModel.DataAnnotations;
//using Wingspan.Web.Mvc.Extensions;
using Wingspan.Web.Mvc.Crud;
namespace BuddyClassGenerator.Models
{
public class ChemicalMetaData
{
[DisplayName("Chemical Id")]
public System.Guid ChemicalId { get; set; }
[DisplayName("Active Ingredient")]
public System.String ActiveIngredient { get; set; }
[DisplayName("Type")]
public System.String Type { get; set; }
[DisplayName("LERAP")]
public System.String LERAP { get; set; }
[DisplayName("Hazard Classification")]
public System.String HazardClassification { get; set; }
[DisplayName("MAPP")]
public System.Int32 MAPP { get; set; }
[DisplayName("Hygiene Practice")]
public System.String HygienePractice { get; set; }
[DisplayName("Medical Advice")]
public System.String MedicalAdvice { get; set; }
[DisplayName("Label")]
public System.String Label { get; set; }
[DisplayName("PPE")]
public System.String PPE { get; set; }
[DisplayName("Warnings")]
public System.String Warnings { get; set; }
[DisplayName("Products")]
public System.Collections.Generic.ICollection<BuddyClassGenerator.Models.Product> Products { get; set; }
}
public partial class Chemical : IInjectItemSL
{
public ItemSL ItemSL
{
get
{
return new ItemSL
{
ItemId = ChemicalId, ItemText = Name
};
}
}
}
您无疑会注意到我已将两个类放在同一个文件中。可能不是最佳实践,但它节省了我的时间和文件夹中的视觉混乱,所以这是我的特权。
待办事项: 1、好友类中不包含导航属性; 2、从属性类型中删除命名空间名称。
我希望这对某人有所帮助,但请记住,要使其正常工作,您需要上面详述的有形 T4 ttincludes。