Một điều cần ghi nhớ là tập lệnh T4 bạn đang viết không thực sự "nằm trong cùng một cụm" - bởi vì đó là mã thời gian thiết kế, không phải mã thời gian chạy. Nói cách khác - nó không được biên dịch vào assembly của bạn ở tất cả.
Thay vào đó, tập lệnh mẫu T4 chạy trong khi bạn đang viết mã - hoặc, nếu bạn đã bật móc nhất định, bất cứ khi nào bạn xây dựng/biên dịch chương trình của mình. Bởi vì nó thực sự là riêng biệt là từ dự án, mã của bạn, không có khả năng tham chiếu trực tiếp đến dự án của bạn - trừ khi bạn sử dụng một cái gì đó như DTE - mang đến cho bạn khả năng truy cập vào môi trường Visual Studio và khám phá các phần tử chẳng hạn như dự án hiện đang được tải.
Như một ví dụ, hãy xem xét kịch bản sau đây:
<#@ 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);
}
}
}
}
kịch bản này, trong bản chất, sẽ chạy qua một không gian tên cụ thể (trong trường hợp này "MyNamespace"
), lặp qua tất cả các lớp học trong đó, sau đó sản lượng một tệp mã mới chỉ liệt kê các thuộc tính công khai của chúng với một getter
/setter
- về bản chất, tạo ra một POCO của các đối tượng. Trong một số dự án của tôi, tôi sử dụng một phiên bản thích hợp của mã này để tạo các đối tượng JavaScript dựa trên các POCO của tôi, để các mô hình JS của tôi luôn có thể đồng bộ với các đối tượng phía máy chủ của tôi, từ phối cảnh tuần tự hóa.
Các mẹo để thực hiện, tuy nhiên, là trong vài dòng đầu tiên:
var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
var project = dte.Solution.FindProjectItem(TransformationContext.Current.Host.TemplateFile).ContainingProject);
var classes = FindClasses(project, targetNamespace, "");
Về bản chất, các dịch vụ DTE được hỏi Visual Studio cung cấp cho nó một mô hình trừu tượng của hiện nạp Solution
và nó Projects
. Sau đó chúng tôi tải lên Project
trong đó TemplateFile
hiện tại được lưu trữ và trong phương thức FindClasses()
, phân tích các lớp trong dự án phù hợp với tiêu chí tìm kiếm của chúng tôi.
Tôi hy vọng các mã ví dụ cho bạn một điểm khởi đầu để nhảy ra khỏi từ - nhưng nếu bạn cần biết thêm chi tiết, đây là một vài tài liệu tham khảo bổ sung để bạn chìm răng của bạn vào:
Tôi đã sử dụng $ (TargetPath) thay thế. Đó là macro cho dll. <# @ assembly name = "$ (TargetPath)" #> <# @ import namespace = "MyAssembly.CodeGeneration" #> –
Sẽ rất tuyệt nếu bạn rời giải pháp gốc –