从命令行转换T4不会将生成的文件添加到csproj

3
我正在尝试使用以下命令行通过TextTransform.exe转换T4模板:

"%ProgramFiles(x86)%\Common Files\Microsoft Shared\TextTemplating\10.0\TextTransform.exe" -out .\MyProj\MyT4.cs -I "%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\Templates\Includes" -a !NamespaceHint!MyNameSpace -dp T4VSHost!Microsoft.Data.Entity.Design.VisualStudio.Directives.FallbackT4VSHostProcessor!"%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\Microsoft.Data.Entity.Design.dll" .\MyProj\MyT4.tt

结果:

  1. 没有错误消息
  2. %ERRORLEVEL%在完成时为0。
  3. 文件已生成
  4. .csproj未更改

问题在于第4点。这可能是预期的,因为.csproj不是上述命令行的一部分,但我找不到可以接受它的任何参数。

我做错了什么或者应该做什么?

P.S. 当我在Visual Studio中使用按钮时,该过程按预期工作(新文件添加到项目中)。


只是一条评论;我个人从不费心从一个模板生成许多文件。这个问题就是其中之一。现在你可能无法选择,因为周围的要求,但对于未来和其他人来说,这是我的信念:一个模板对应一个文件=>少些问题。 - Just another metaprogrammer
这些文件是使用数据库数据生成的,并用于将通用数据仓库转换为强类型域驱动结构。这些文件的数量不是固定的。我每个输出文件类型有一个ttinclude文件,一个主ttinclude文件来编排所有部分和tt文件只是将参数传递给编排器ttinclude文件。 - Danny Varod
2个回答

2
以下方法解决:
  1. 在命令行中添加以下参数:

    -a !!ProjPath!.\MyProj\MyProj.csproj -a !!T4Path!.\MyProj\MyT4.tt

  2. 将包含目录参数更改为本地路径:

    -I ".\Dependencies"

  3. EF.Utility.CS.ttinclude复制到该路径并进行以下更改:

3.1. 替换:

    public static EntityFrameworkTemplateFileManager Create(object textTransformation)
    {
        DynamicTextTransformation transformation = DynamicTextTransformation.Create(textTransformation);
        IDynamicHost host = transformation.Host;

#if !PREPROCESSED_TEMPLATE
        if (host.AsIServiceProvider() != null)
        {
            return new VsEntityFrameworkTemplateFileManager(transformation);
        }
#endif
        return new EntityFrameworkTemplateFileManager(transformation);
    }

使用

    public static EntityFrameworkTemplateFileManager Create(object textTransformation)
    {
        DynamicTextTransformation transformation = DynamicTextTransformation.Create(textTransformation);
        IDynamicHost host = transformation.Host;

#if !PREPROCESSED_TEMPLATE
        if (host.AsIServiceProvider() != null)
        {
            return new VsEntityFrameworkTemplateFileManager(transformation);
        }
#endif
        return new EFTemplateFileManagerPlus(transformation);
    }

(上次返回已更改)

将此类添加到文件中:

private sealed class EFTemplateFileManagerPlus : EntityFrameworkTemplateFileManager
{
        private Action<IEnumerable<string>> projectSyncAction;
        private readonly string _projPath;
        private readonly string _t4Name;

        public EFTemplateFileManagerPlus(object textTemplating)
            : base(textTemplating)
        {
            var projPath = _textTransformation.Host.ResolveParameterValue("", "", "ProjPath");
            var t4Path = _textTransformation.Host.ResolveParameterValue("", "", "T4Path");
            _projPath = System.IO.Path.GetFullPath(projPath);
            _t4Name = System.IO.Path.GetFileName(t4Path);

            projectSyncAction = files => SyncCsProjFile(_projPath, _t4Name, files);
        }

        public static void SyncCsProjFile(string csProjFilePath, string t4FileName, IEnumerable<string> files)
        {
            files = files.Select(f => System.IO.Path.GetFileName(f)).Distinct().ToList();

            var csProjDocument = new XmlDocument();
            csProjDocument.Load(csProjFilePath);

            var root = csProjDocument.DocumentElement;

            XmlElement itemGroup = root.ChildNodes.OfType<XmlElement>()
                .Where(n => n.Name == "ItemGroup")
                .SelectMany(n => n.ChildNodes.OfType<XmlNode>()
                    .Where(c => c.Name == "Compile")
                    )
                .Select(c => c.ParentNode)
                .FirstOrDefault() as XmlElement;

            if (itemGroup == null)
            {
                itemGroup = csProjDocument.CreateNode(XmlNodeType.Element, "ItemGroup", null) as XmlElement;
                root.AppendChild(itemGroup);
            }

            var codeFiles = itemGroup.ChildNodes.OfType<XmlElement>()
                .Where(c =>
                    c.Name == "Compile"
                    && c.HasAttribute("Include") && !String.IsNullOrEmpty(c.GetAttribute("Include")))
                .ToList();

            var dependantFiles = codeFiles
                .Where(f =>
                    f.ChildNodes.OfType<XmlElement>().Any(c =>
                        c.Name == "DependentUpon"
                        && c.InnerText == t4FileName)
                ).ToList();

            // Remove redundant files
            foreach (var node in dependantFiles)
            {
                if (!files.Contains(node.GetAttribute("Include")))
                    itemGroup.RemoveChild(node);
            }

            // Add missing files
            foreach (var name in files)
            {
                if (!dependantFiles.Any(node => node.GetAttribute("Include") == name))
                {
                    var node = csProjDocument.CreateNode(XmlNodeType.Element, "Compile", null) as XmlElement;
                    node.SetAttribute("Include", name);
                    itemGroup.AppendChild(node);

                    var node2 = csProjDocument.CreateNode(XmlNodeType.Element, "DependentUpon", null) as XmlElement;
                    node2.InnerText = t4FileName;
                    node.AppendChild(node2);
                }
            }

            SaveClean(csProjDocument, csProjFilePath);
        }

        static private void SaveClean(XmlDocument doc, string path)
        {
            StringBuilder sb = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = Encoding.UTF8;
            settings.Indent = true;
            settings.IndentChars = "  ";
            settings.NewLineChars = "\r\n";
            settings.NewLineHandling = NewLineHandling.Replace;
            settings.NamespaceHandling = NamespaceHandling.OmitDuplicates;
            using (XmlWriter writer = XmlWriter.Create(sb, settings))
            {
                doc.Save(writer);
            }

            var newXml = sb.ToString().Replace("encoding=\"utf-16\"", "encoding=\"utf-8\"").Replace(" xmlns=\"\"", string.Empty);
            System.IO.File.WriteAllText(path, newXml, Encoding.UTF8);
        }

        public override IEnumerable<string> Process(bool split)
        {
            var generatedFileNames = base.Process(split);

            projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));

            return generatedFileNames;
        }
    }

现在项目文件同步也可以使用 TextTransform.exe 进行操作。

0

我相信命令行主机无法更改 .csproj 文件,只有通过访问 DTE 对象才能由 VS 主机执行。


那么 TextTransform.exe 无法配置以访问 EnvDTE?在这种情况下,有没有通过 VS 主机在构建过程中访问转换的方法? - Danny Varod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接