我正在尝试从命令行自动化我的构建。不幸的是,我的项目从一开始就没有自动化,现在我正在努力使用 T4模板。
在尝试使用 MSBuild 转换T4模板时发现了几个错误(this和this)后,我决定从根目录找到所有* .tt文件我的项目并逐个应用 TextTransform 。
但......其中一些有点问题。对那些人运行TextTransform时,我收到以下消息:
C:\ Specifications.tt(0,0) :错误:运行转换:System.InvalidCastException:不能 兑换 '的 Microsoft.VisualStudio.TextTemplating.CommandLine.CommandLineHost ' 进入' System.IServiceProvider '。恩 Microsoft.VisualStudio.TextTemplating7ea01e60423f49d2871739be33c0c810.GeneratedTextTransformation.GetProject() 在 Microsoft.VisualStudio.TextTemplating7ea01e60423f49d2871739be33c0c810.GeneratedTextTransformation.FindClasses(字符串 nameSpace,String className,String baseClassName)in Microsoft.VisualStudio.TextTemplating7ea01e60423f49d2871739be33c0c810.GeneratedTextTransformation.TransformText()
打开.tt并查找IServiceProvider或CommandLineHost,我发现了这两种方法:
private Project GetProject()
{
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
DTE dte = serviceProvider.GetService(typeof(DTE)) as DTE;
// Get DTE
//var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
// Get ProjectItem representing the template file
ProjectItem projectItem = dte.Solution.FindProjectItem(this.Host.TemplateFile);
// Get the Project of the template file
Project project = projectItem.ContainingProject;
return project;
}
private string GetDefaultNamespace()
{
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
DTE dte = serviceProvider.GetService(typeof(DTE)) as DTE;
// Get DTE
//var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
// Get ProjectItem representing the template file
ProjectItem projectItem = dte.Solution.FindProjectItem(this.Host.TemplateFile);
// Get the Project of the template file
Project project = projectItem.ContainingProject;
var vsSolution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution));
IVsHierarchy vsHierarchy;
ErrorHandler.ThrowOnFailure(vsSolution.GetProjectOfUniqueName(project.FullName, out vsHierarchy));
uint projectItemId;
ErrorHandler.ThrowOnFailure(vsHierarchy.ParseCanonicalName(projectItem.FileNames[1], out projectItemId));
object defaultNamespace;
ErrorHandler.ThrowOnFailure(vsHierarchy.GetProperty(projectItemId, (int)VsHierarchyPropID.DefaultNamespace, out defaultNamespace));
return ((string)defaultNamespace);
}
是否有可能以某种方式解决此问题?使用替代方法更改.tt代码...
我只是想从命令行自动化我的构建版本!
更新 以下是模板的全部内容:
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.11.0" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.Shell" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ include file="EF.Utility.CS.ttinclude"#>
<#@ output extension="/"#>
<#
CodeGenerationTools code = new CodeGenerationTools(this);
MetadataLoader loader = new MetadataLoader(this);
CodeRegion region = new CodeRegion(this, 1);
MetadataTools ef = new MetadataTools(this);
string namespaceName = code.VsNamespaceSuggestion();// @"ArcNet.Piloto.Domain.Repositories";
string filenameSuffix = "Repository.cs";
// include additional using statements here
List<string> usingList = new List<string>(){
"IDB.MW.Domain.Entities",
"EverNext.Domain.Contracts.Repositories",
"System.ServiceModel",
"System.CodeDom.Compiler",
"EverNext.Domain.Contracts.Model"
// ,"other namespace"
};
////////////////////////////////////////////////////////////////////////////// Don't edit from here
EntityFrameworkTemplateFileManager fileManager = EntityFrameworkTemplateFileManager.Create(this);
// Write out support code to primary template output file
WriteHeader(fileManager, usingList.ToArray());
BeginNamespace(namespaceName, code);
EndNamespace(namespaceName);
// Emit Entity Types
foreach (var classes in FindClasses("IDB.MW.Domain.Entities", "", "BaseEntity").Where(c=>c.ImplementedInterfaces.OfType<CodeInterface>()
.Any(d=>d.Name.Equals("IAggregateRoot"))))
{
fileManager.StartNewFile(classes.Name + filenameSuffix);
BeginNamespace(namespaceName, code);
#>
[ServiceContract]
<#="public"#> <#=code.SpaceAfter(classes.IsAbstract ? "abstract" : string.Empty)#>partial interface I<#=classes.Name#>Repository : IEntityRepository<<#=classes.Name#>>,IAggregateRoot
{
}
<#
EndNamespace(namespaceName);
}
//
//if (!VerifyTypesAreCaseInsensitiveUnique(ItemCollection))
//{
//return "";
//}
fileManager.Process();
#>
<#+
void WriteHeader(EntityFrameworkTemplateFileManager fileManager, params string[] extraUsings)
{
fileManager.StartHeader();
#>
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Linq;
<#=String.Join(String.Empty, extraUsings.Select(u => "using " + u + ";" + Environment.NewLine).ToArray())#>
<#+
fileManager.EndBlock();
}
void BeginNamespace(string namespaceName, CodeGenerationTools code)
{
CodeRegion region = new CodeRegion(this);
if (!String.IsNullOrEmpty(namespaceName))
{
#>
namespace <#=code.EscapeNamespace(namespaceName)#>
{
<#+
PushIndent(CodeRegion.GetIndent(1));
}
}
void EndNamespace(string namespaceName)
{
if (!String.IsNullOrEmpty(namespaceName))
{
PopIndent();
#>
}
<#+
}
}
bool VerifyTypesAreCaseInsensitiveUnique(EdmItemCollection itemCollection)
{
Dictionary<string, bool> alreadySeen = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
foreach(StructuralType type in itemCollection.GetItems<StructuralType>())
{
if (!(type is EntityType || type is ComplexType))
{
continue;
}
if (alreadySeen.ContainsKey(type.FullName))
{
Error(String.Format(CultureInfo.CurrentCulture, "This template does not support types that differ only by case, the types {0} are not supported", type.FullName));
return false;
}
else
{
alreadySeen.Add(type.FullName, true);
}
}
return true;
}
private System.Collections.Generic.List<CodeClass> FindClasses(string nameSpace, string className, string baseClassName)
{
System.Collections.Generic.List<CodeClass> result = new System.Collections.Generic.List<CodeClass>();
FindClasses(GetProject().CodeModel.CodeElements, className, baseClassName, nameSpace, result, false);
return result;
}
private void FindClasses(CodeElements elements, string className, string baseClassName, string searchNamespace, List<CodeClass> result, bool isNamespaceOk)
{
if (elements == null) return;
foreach (CodeElement element in elements)
{
if (element is EnvDTE.CodeNamespace)
{
EnvDTE.CodeNamespace ns = element as EnvDTE.CodeNamespace;
if (ns != null)
{
if (ns.FullName == searchNamespace)
FindClasses(ns.Members, className, baseClassName, searchNamespace, result, true);
else
FindClasses(ns.Members, className, baseClassName, searchNamespace, result, false);
}
}
else if (element is CodeClass && isNamespaceOk)
{
CodeClass c = element as CodeClass;
if (c != null)
{
if (c.FullName.Contains(className) && (baseClassName == null || (HasIt(c.Bases, baseClassName) && c.Name != baseClassName)))
result.Add(c);
FindClasses(c.Members, className, baseClassName, searchNamespace, result, true);
}
}
}
}
private bool HasIt(CodeElements elements, string name)
{
foreach (CodeElement element in elements)
{
CodeClass c = element as CodeClass;
if (c != null && c.Bases != null)
{
if (HasIt(c.Bases, name))
{
return true;
}
}
if (element.Name == name)
return true;
}
return false;
}
private Project GetProject()
{
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
DTE dte = serviceProvider.GetService(typeof(DTE)) as DTE;
// Get DTE
//var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
// Get ProjectItem representing the template file
ProjectItem projectItem = dte.Solution.FindProjectItem(this.Host.TemplateFile);
// Get the Project of the template file
Project project = projectItem.ContainingProject;
return project;
}
private string GetDefaultNamespace()
{
IServiceProvider serviceProvider = (IServiceProvider)this.Host;
DTE dte = serviceProvider.GetService(typeof(DTE)) as DTE;
// Get DTE
//var dte = (DTE)TransformationContext.Current.GetService(typeof(DTE));
// Get ProjectItem representing the template file
ProjectItem projectItem = dte.Solution.FindProjectItem(this.Host.TemplateFile);
// Get the Project of the template file
Project project = projectItem.ContainingProject;
var vsSolution = (IVsSolution)serviceProvider.GetService(typeof(SVsSolution));
IVsHierarchy vsHierarchy;
ErrorHandler.ThrowOnFailure(vsSolution.GetProjectOfUniqueName(project.FullName, out vsHierarchy));
uint projectItemId;
ErrorHandler.ThrowOnFailure(vsHierarchy.ParseCanonicalName(projectItem.FileNames[1], out projectItemId));
object defaultNamespace;
ErrorHandler.ThrowOnFailure(vsHierarchy.GetProperty(projectItemId, (int)VsHierarchyPropID.DefaultNamespace, out defaultNamespace));
return ((string)defaultNamespace);
}
#>
答案 0 :(得分:0)
我们不知道您的模板是什么样的 - 包括它使用的内容,它依赖的其他技术或模板,但很可能您在模板中不需要任何DTE(Visual Studio)功能。
此模板尝试使用DTE从项目项中读取“命名空间”字段,这在命令行构建中是不可能的。要解决此问题,只需更改模板并在模板中对命名空间字符串进行硬编码。
像这样的导航示例
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".generated.cs" #>
namespace Packaging {
internal partial class FileSystem { }
}
或者您可以让模板导入另一个模板(在此示例中为MyInclude.ttinclude)并调用该导入中定义的方法(Generate),该方法将命名空间作为参数,然后在代码生成中使用。
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension="generated.cs" #>
<#@ include file="..\MyInclude.ttinclude"#>
<#@ Generate("MyNamespace", false); #>