• Multiple outputs from T4 made easy revisited (T4 输出多个文件)


    来源:http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited

    My multiple outputs from t4 made easy post contained a class making it easy to produce multiple files from Visual Studio’s text templating engine (T4).

    While useful it had a few issues:

    • Getting start/end blocks mixed up resulted in unpredictable behaviour
    • Files were rewritten even when content did not change
    • Did not play well with source control
    • Files not always deleted in VS
    • Failed in Visual Studio’s project-less Web Sites

    This helper class forms the basis of multiple file output for Entity Framework templates in .NET 4.0 and the LINQ to SQL templates on CodePlex so we (Jeff Reed, Andrew Peters and myself) made the following changes.

    Improvements

    Simpler block handling

    The header, footer and file blocks can now be completed with EndBlock (EndHeader and EndFooter are gone), although it will automatically end the previous block when it hits a new one or the final Process method.

    Skip unchanged files

    Files are now only written to disk if the contents are different with the exception of the original T4 output file (we can’t stop that, sorry).

    There is additional overhead reading and comparing files we believe unmodified files keeping their dates and source control status are worth it.

    Automatic checkout

    When the template detects it is running in Visual Studio and that the file it needs to write to is currently in source control but not checked out it will check the file out for you.

    Predictable clean-up

    All files that were not part of the generation process but are nested under the project item will now be deleted when running inside Visual Studio.

    Outside of Visual Studio files are no longer deleted – this was destructive and it couldn’t know which files it generated on a previous run to clean-up correctly anyway.

    Website projects fall back to single file generation

    Visual Studio has both web sites and web applications with the former being project-less leading to very messy multi-file generation so it forces single file generation.

    Internal improvements

    Source is now simpler to read and understand with less public visibility and faster and more robust VS interop by batching the files & deletes to a single invoke at the end to avoid conflicts with other add-in’s that might be triggered by the changes.

    Usage

    Initialization

    You’ll need to get the code into your template – either copy the code in or reference it with an include directive. Then declare an instance of the Manager class passing in some environmental options such as the desired default output path. (For Visual Studio 2010 remove the #v3.5 portion from the language attribute)

    <#@ template language="C#v3.5" hostspecific="True" #>
    <#@include file="Manager.ttinclude" #>
    <# var manager = Manager.Create(Host, GenerationEnvironment); #>

    File blocks

    Then add one line before and one line after each block which could be split out into it’s own file passing in what the filename would be if split. The EndBlock is optional if you want it to carry through to the next one :)

    <# manager.StartNewFile("Employee.generated.cs"); #>
    public class Employee { … }
    <# manager.EndBlock(); #>

    Headers & footers

    Many templates need to share a common header/footer for such things as comments or using/import statements or turning on/off warnings. Simply use StartHeader and StartFooter and the blocks will be emitted to the start and end of all split files as well as being left in the original output file.

    <# manager.StartHeader(); #>
    // Code generated by a template
    using System;
    <# manager.EndBlock(); #>

    Process

    At the end of the template call Process to handle splitting the files (true) or not (false). Anything not included in a specific StartNewFile block will remain in the original output file.

    <# manager.Process(true); #>

    Revised Manager class

    <#@ assembly name="System.Core"#>
    <#@ assembly name="System.Data.Linq"#>
    <#@ assembly name="EnvDTE"#>
    <#@ assembly name="System.Xml"#>
    <#@ assembly name="System.Xml.Linq"#>
    <#@ import namespace="System"#>
    <#@ import namespace="System.CodeDom"#>
    <#@ import namespace="System.CodeDom.Compiler"#>
    <#@ import namespace="System.Collections.Generic"#>
    <#@ import namespace="System.Data.Linq"#>
    <#@ import namespace="System.Data.Linq.Mapping"#>
    <#@ import namespace="System.IO"#>
    <#@ import namespace="System.Linq"#>
    <#@ import namespace="System.Reflection"#>
    <#@ import namespace="System.Text"#>
    <#@ import namespace="System.Xml.Linq"#>
    <#@ import namespace="Microsoft.VisualStudio.TextTemplating"#>
    <#+
    // Manager class records the various blocks so it can split them up
    class Manager {
    private class Block {
    public String Name;
    public int Start, Length;
    }
    private Block currentBlock;
    private List<Block> files = new List<Block>();
    private Block footer = new Block();
    private Block header = new Block();
    private ITextTemplatingEngineHost host;
    private StringBuilder template;
    protected List<String> generatedFileNames = new List<String>();
    public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
    return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
    }
    public void StartNewFile(String name) {
    if (name == null)
    throw new ArgumentNullException("name");
    CurrentBlock
    = new Block { Name = name };
    }
    public void StartFooter() {
    CurrentBlock
    = footer;
    }
    public void StartHeader() {
    CurrentBlock
    = header;
    }
    public void EndBlock() {
    if (CurrentBlock == null)
    return;
    CurrentBlock.Length
    = template.Length - CurrentBlock.Start;
    if (CurrentBlock != header && CurrentBlock != footer)
    files.Add(CurrentBlock);
    currentBlock
    = null;
    }
    public virtual void Process(bool split) {
    if (split) {
    EndBlock();
    String headerText
    = template.ToString(header.Start, header.Length);
    String footerText
    = template.ToString(footer.Start, footer.Length);
    String outputPath
    = Path.GetDirectoryName(host.TemplateFile);
    files.Reverse();
    foreach(Block block in files) {
    String fileName
    = Path.Combine(outputPath, block.Name);
    String content
    = headerText + template.ToString(block.Start, block.Length) + footerText;
    generatedFileNames.Add(fileName);
    CreateFile(fileName, content);
    template.Remove(block.Start, block.Length);
    }
    }
    }
    protected virtual void CreateFile(String fileName, String content) {
    if (IsFileContentDifferent(fileName, content))
    File.WriteAllText(fileName, content);
    }
    public virtual String GetCustomToolNamespace(String fileName) {
    return null;
    }
    public virtual String DefaultProjectNamespace {
    get { return null; }
    }
    protected bool IsFileContentDifferent(String fileName, String newContent) {
    return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
    }
    private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
    this.host = host;
    this.template = template;
    }
    private Block CurrentBlock {
    get { return currentBlock; }
    set {
    if (CurrentBlock != null)
    EndBlock();
    if (value != null)
    value.Start
    = template.Length;
    currentBlock
    = value;
    }
    }
    private class VSManager: Manager {
    private EnvDTE.ProjectItem templateProjectItem;
    private EnvDTE.DTE dte;
    private Action<String> checkOutAction;
    private Action<IEnumerable<String>> projectSyncAction;
    public override String DefaultProjectNamespace {
    get {
    return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
    }
    }
    public override String GetCustomToolNamespace(string fileName) {
    return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
    }
    public override void Process(bool split) {
    if (templateProjectItem.ProjectItems == null)
    return;
    base.Process(split);
    projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames,
    null, null));
    }
    protected override void CreateFile(String fileName, String content) {
    if (IsFileContentDifferent(fileName, content)) {
    CheckoutFileIfRequired(fileName);
    File.WriteAllText(fileName, content);
    }
    }
    internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
    :
    base(host, template) {
    var hostServiceProvider
    = (IServiceProvider) host;
    if (hostServiceProvider == null)
    throw new ArgumentNullException("Could not obtain IServiceProvider");
    dte
    = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE));
    if (dte == null)
    throw new ArgumentNullException("Could not obtain DTE from host");
    templateProjectItem
    = dte.Solution.FindProjectItem(host.TemplateFile);
    checkOutAction
    = (String fileName) => dte.SourceControl.CheckOutItem(fileName);
    projectSyncAction
    = (IEnumerable<String> keepFileNames) => ProjectSync(templateProjectItem, keepFileNames);
    }
    private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, IEnumerable<String> keepFileNames) {
    var keepFileNameSet
    = new HashSet<String>(keepFileNames);
    var projectFiles
    = new Dictionary<String, EnvDTE.ProjectItem>();
    var originalFilePrefix
    = Path.GetFileNameWithoutExtension(templateProjectItem.get_FileNames(0)) + ".";
    foreach(EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
    projectFiles.Add(projectItem.get_FileNames(
    0), projectItem);
    // Remove unused items from the project
    foreach(var pair in projectFiles)
    if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
    pair.Value.Delete();
    // Add missing files to the project
    foreach(String fileName in keepFileNameSet)
    if (!projectFiles.ContainsKey(fileName))
    templateProjectItem.ProjectItems.AddFromFile(fileName);
    }
    private void CheckoutFileIfRequired(String fileName) {
    var sc
    = dte.SourceControl;
    if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
    checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName,
    null, null));
    }
    }
    } #
    >

  • 相关阅读:
    零位扩展和符号位扩展
    JAVA虚拟机20基于栈的解释器执行过程示例
    JAVA虚拟机16方法的动态调用
    JAVA虚拟机17栈帧(局部变量表操作数栈动态连接返回地址)
    JAVA虚拟机21JAVA内存模型
    JAVA虚拟机18方法调用
    符号扩展和零位扩展
    JAVA虚拟机22原子性、可见性与有序性、先行发生原则
    MYSQL各版本下载,包括linux和windows
    【转】Android模拟器怎么配置网络连通
  • 原文地址:https://www.cnblogs.com/gdjlc/p/2086871.html
Copyright © 2020-2023  润新知