Одна вещь, чтобы иметь в виду, что T4 скрипт вы пишете на самом деле не «находятся в той же сборке» - потому что во время разработки кода, а не во время выполнения кода. Другими словами - он не скомпилируется в вашу сборку вообще.
Вместо этого скрипт шаблона T4 запускается во время написания кода - или, если у вас есть определенные перехватчики, всякий раз, когда вы создаете/компилируете свою программу. Поскольку на самом деле это из вашего проекта, код, он не имеет возможности напрямую ссылаться на сборку вашего проекта - если вы не используете что-то вроде DTE, что дает вам возможность доступа к самой среде Visual Studio и изучить элементы таких как загруженный в настоящий момент проект.
В качестве примера рассмотрим следующий сценарий:
<#@ template language="C#" debug="false" hostspecific="true" #>
<#@ output extension=".js" #>
<#@ assembly name="System" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="System.Data.Entity" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ include file="T4Toolbox.tt" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>
string targetNamespace = "MyNamespace";
var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
var project = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile).ContainingProject);
var classes = FindClasses(project, targetNamespace, "");
<# foreach (CodeClass c in classes) { #>
public class <#= c.Name #> {
<# var properties = c.Members.OfType<EnvDTE.CodeProperty>()
.Where(p => p.Access.HasFlag(vsCMAccess.vsCMAccessPublic))
.OrderBy(p => p.Name);
foreach (var prop in properties) {
#>
public <#= prop.Type.AsString #> <#= prop.Name #> { get; set; }
<# } #>
}
<# } #>
<#+ List<CodeClass> FindClasses(Project project, string ns, string className) {
List<CodeClass> result = new List<CodeClass>();
FindClasses(project.CodeModel.CodeElements, className, ns, result, false);
return result;
}
void FindClasses(CodeElements elements, string className, string searchNamespace, List<CodeClass> result, bool isNamespaceOk) {
if (elements == null) return;
foreach (CodeElement element in elements) {
if (element is CodeNamespace) {
CodeNamespace ns = element as CodeNamespace;
if (ns != null) {
if (ns.FullName == searchNamespace)
FindClasses(ns.Members, className, searchNamespace, result, true);
else
FindClasses(ns.Members, className, searchNamespace, result, false);
}
} else if (element is CodeClass && isNamespaceOk) {
CodeClass c = element as CodeClass;
if (c != null) {
if (c.FullName.Contains(className))
result.Add(c);
FindClasses(c.Members, className, searchNamespace, result, true);
}
}
}
}
Этот сценарий, по сути, будет выполняться через определенный имен (в данном случае "MyNamespace"
), перебирать все классы в ней, а затем выводить новый файл кода, в котором перечислены только их общедоступные свойства с getter
/setter
- по существу, создавая POCO объектов. В некоторых моих проектах я использую адаптированную версию этого кода для генерации объектов JavaScript на основе моих POCOs, так что мои JS-модели всегда могут быть синхронизированы с моими серверными объектами с точки зрения сериализации.
Хитрость к нему, хотя, в первые несколько строк:
var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
var project = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile).ContainingProject);
var classes = FindClasses(project, targetNamespace, "");
В сущности, служба DTE просит Visual Studio, чтобы придать ему абстрактную модель текущей загруженной Solution
и это Projects
, Затем мы загружаем Project
, в котором хранится текущий TemplateFile
, и в методе FindClasses()
проанализируйте классы внутри этого проекта, которые соответствуют нашим критериям поиска.
Я надеюсь, что пример кода дает вам отправную точку, чтобы спрыгнуть, - но если вам нужно более подробно, вот несколько дополнительных ссылок для вас, чтобы погрузить Ваши зубы в:
Вместо этого я использовал $ (TargetPath). Это макрос для dll. <# @ assembly name = "$ (TargetPath)" #> <# @ import namespace = "MyAssembly.CodeGeneration" #> –
Было бы неплохо, если бы вы также оставили исходное решение –