Aracılığıyla paylaş


İzlenecek yol: Özel Yönerge İşlemcisi Oluşturma

Yönerge işlemcileri, oluşturulan dönüştürme sınıfına kod ekleyerek çalışır. Bir metin şablonundan yönerge çağırırsanız, metin şablonunuzda yazdığınız kodun geri kalanı yönergenin sağladığı işlevlere bağlı olabilir.

Kendi özel yönerge işlemcilerinizi yazabilirsiniz. Bu metin şablonlarınızı özelleştirmenize olanak sağlar. Özel yönerge işlemcisi oluşturmak için veya RequiresProvidesDirectiveProcessoröğesinden DirectiveProcessor devralan bir sınıf oluşturursunuz.

Bu kılavuzda gösterilen görevler aşağıdakileri içerir:

  • Özel yönerge işlemcisi oluşturma

  • Yönerge işlemcisini kaydetme

  • Yönerge işlemcisini test edin

Özel Yönerge İşlemcisi Oluşturma

Bu kılavuzda, özel bir yönerge işlemcisi oluşturursunuz. XML dosyasını okuyan, bir değişkende depolayan ve bir özellik aracılığıyla kullanıma sunan özel bir XmlDocument yönerge eklersiniz. "Yönerge İşlemcisini Test Etme" bölümünde, XML dosyasına erişmek için metin şablonunda bu özelliği kullanırsınız.

Özel yönergenize yaptığınız çağrı aşağıdaki gibi görünür:

<#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<Your Path>DocFile.xml" #>

Özel yönerge işlemcisi, değişkeni ve özelliği oluşturulan dönüştürme sınıfına ekler. Yazdığınız yönerge, altyapının System.CodeDom oluşturulan dönüştürme sınıfına eklediği kodu oluşturmak için sınıfları kullanır. SınıflarSystem.CodeDom, yönergenin parametresinde template belirtilen dile bağlı olarak Visual C# veya Visual Basic'te language kod oluşturur. Yönerge işlemcisinin dili ve yönerge işlemcisine erişen metin şablonunun dilinin eşleşmesi gerekmez.

Yönergenin oluşturduğu kod aşağıdaki gibi görünür:

private System.Xml.XmlDocument document0Value;

public virtual System.Xml.XmlDocument Document0
{
  get
  {
    if ((this.document0Value == null))
    {
      this.document0Value = XmlReaderHelper.ReadXml(<FileNameParameterValue>);
    }
    return this.document0Value;
  }
}

Özel yönerge işlemcisi oluşturmak için

  1. Visual Studio'da, CustomDP adlı bir C# veya Visual Basic kitaplık projesi oluşturun.

    Not

    Yönerge işlemcisini birden fazla bilgisayara yüklemek istiyorsanız, Visual Studio Uzantısı (VSIX) projesi kullanmak ve uzantıya bir .pkgdef dosyası eklemek daha iyidir. Daha fazla bilgi için bkz . Özel Yönerge İşlemcisi Dağıtma.

  2. Aşağıdaki derlemelere başvurular ekleyin:

    • Microsoft.VisualStudio.TextTemplating.*.0

    • Microsoft.VisualStudio.TextTemplating.Interfaces.*.0

  3. Sınıf1'deki kodu aşağıdaki kodla değiştirin. Bu kod, sınıfından DirectiveProcessor devralan ve gerekli yöntemleri uygulayan bir CustomDirectiveProcessor sınıfı tanımlar.

    using System;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using Microsoft.VisualStudio.TextTemplating;
    
    namespace CustomDP
    {
        public class CustomDirectiveProcessor : DirectiveProcessor
        {
            // This buffer stores the code that is added to the
            // generated transformation class after all the processing is done.
            // ---------------------------------------------------------------------
            private StringBuilder codeBuffer;
    
            // Using a Code Dom Provider creates code for the
            // generated transformation class in either Visual Basic or C#.
            // If you want your directive processor to support only one language, you
            // can hard code the code you add to the generated transformation class.
            // In that case, you do not need this field.
            // --------------------------------------------------------------------------
            private CodeDomProvider codeDomProvider;
    
            // This stores the full contents of the text template that is being processed.
            // --------------------------------------------------------------------------
            private String templateContents;
    
            // These are the errors that occur during processing. The engine passes
            // the errors to the host, and the host can decide how to display them,
            // for example the host can display the errors in the UI
            // or write them to a file.
            // ---------------------------------------------------------------------
            private CompilerErrorCollection errorsValue;
            public new CompilerErrorCollection Errors
            {
                get { return errorsValue; }
            }
    
            // Each time this directive processor is called, it creates a new property.
            // We count how many times we are called, and append "n" to each new
            // property name. The property names are therefore unique.
            // -----------------------------------------------------------------------------
            private int directiveCount = 0;
    
            public override void Initialize(ITextTemplatingEngineHost host)
            {
                // We do not need to do any initialization work.
            }
    
            public override void StartProcessingRun(CodeDomProvider languageProvider, String templateContents, CompilerErrorCollection errors)
            {
                // The engine has passed us the language of the text template
                // we will use that language to generate code later.
                // ----------------------------------------------------------
                this.codeDomProvider = languageProvider;
                this.templateContents = templateContents;
                this.errorsValue = errors;
    
                this.codeBuffer = new StringBuilder();
            }
    
            // Before calling the ProcessDirective method for a directive, the
            // engine calls this function to see whether the directive is supported.
            // Notice that one directive processor might support many directives.
            // ---------------------------------------------------------------------
            public override bool IsDirectiveSupported(string directiveName)
            {
                if (string.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    return true;
                }
                if (string.Compare(directiveName, "SuperCoolDirective", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    return true;
                }
                return false;
            }
    
            public override void ProcessDirective(string directiveName, IDictionary<string, string> arguments)
            {
                if (string.Compare(directiveName, "CoolDirective", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    string fileName;
    
                    if (!arguments.TryGetValue("FileName", out fileName))
                    {
                        throw new DirectiveProcessorException("Required argument 'FileName' not specified.");
                    }
    
                    if (string.IsNullOrEmpty(fileName))
                    {
                        throw new DirectiveProcessorException("Argument 'FileName' is null or empty.");
                    }
    
                    // Now we add code to the generated transformation class.
                    // This directive supports either Visual Basic or C#, so we must use the
                    // System.CodeDom to create the code.
                    // If a directive supports only one language, you can hard code the code.
                    // --------------------------------------------------------------------------
    
                    CodeMemberField documentField = new CodeMemberField();
    
                    documentField.Name = "document" + directiveCount + "Value";
                    documentField.Type = new CodeTypeReference(typeof(XmlDocument));
                    documentField.Attributes = MemberAttributes.Private;
    
                    CodeMemberProperty documentProperty = new CodeMemberProperty();
    
                    documentProperty.Name = "Document" + directiveCount;
                    documentProperty.Type = new CodeTypeReference(typeof(XmlDocument));
                    documentProperty.Attributes = MemberAttributes.Public;
                    documentProperty.HasSet = false;
                    documentProperty.HasGet = true;
    
                    CodeExpression fieldName = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), documentField.Name);
                    CodeExpression booleanTest = new CodeBinaryOperatorExpression(fieldName, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null));
                    CodeExpression rightSide = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression("XmlReaderHelper"), "ReadXml", new CodePrimitiveExpression(fileName));
                    CodeStatement[] thenSteps = new CodeStatement[] { new CodeAssignStatement(fieldName, rightSide) };
    
                    CodeConditionStatement ifThen = new CodeConditionStatement(booleanTest, thenSteps);
                    documentProperty.GetStatements.Add(ifThen);
    
                    CodeStatement s = new CodeMethodReturnStatement(fieldName);
                    documentProperty.GetStatements.Add(s);
    
                    CodeGeneratorOptions options = new CodeGeneratorOptions();
                    options.BlankLinesBetweenMembers = true;
                    options.IndentString = "    ";
                    options.VerbatimOrder = true;
                    options.BracingStyle = "C";
    
                    using (StringWriter writer = new StringWriter(codeBuffer, CultureInfo.InvariantCulture))
                    {
                        codeDomProvider.GenerateCodeFromMember(documentField, writer, options);
                        codeDomProvider.GenerateCodeFromMember(documentProperty, writer, options);
                    }
                }
    
                // One directive processor can contain many directives.
                // If you want to support more directives, the code goes here...
                // -----------------------------------------------------------------
                if (string.Compare(directiveName, "supercooldirective", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    // Code for SuperCoolDirective goes here...
                }
    
                // Track how many times the processor has been called.
                // -----------------------------------------------------------------
                directiveCount++;
    
            }
    
            public override void FinishProcessingRun()
            {
                this.codeDomProvider = null;
    
                // Important: do not do this:
                // The get methods below are called after this method
                // and the get methods can access this field.
                // -----------------------------------------------------------------
                // this.codeBuffer = null;
            }
    
            public override string GetPreInitializationCodeForProcessingRun()
            {
                // Use this method to add code to the start of the
                // Initialize() method of the generated transformation class.
                // We do not need any pre-initialization, so we will just return "".
                // -----------------------------------------------------------------
                // GetPreInitializationCodeForProcessingRun runs before the
                // Initialize() method of the base class.
                // -----------------------------------------------------------------
                return String.Empty;
            }
    
            public override string GetPostInitializationCodeForProcessingRun()
            {
                // Use this method to add code to the end of the
                // Initialize() method of the generated transformation class.
                // We do not need any post-initialization, so we will just return "".
                // ------------------------------------------------------------------
                // GetPostInitializationCodeForProcessingRun runs after the
                // Initialize() method of the base class.
                // -----------------------------------------------------------------
                return String.Empty;
            }
    
            public override string GetClassCodeForProcessingRun()
            {
                //Return the code to add to the generated transformation class.
                // -----------------------------------------------------------------
                return codeBuffer.ToString();
            }
    
            public override string[] GetReferencesForProcessingRun()
            {
                // This returns the references that we want to use when
                // compiling the generated transformation class.
                // -----------------------------------------------------------------
                // We need a reference to this assembly to be able to call
                // XmlReaderHelper.ReadXml from the generated transformation class.
                // -----------------------------------------------------------------
                return new string[]
                {
                    "System.Xml",
                    this.GetType().Assembly.Location
                };
            }
    
            public override string[] GetImportsForProcessingRun()
            {
                //This returns the imports or using statements that we want to
                //add to the generated transformation class.
                // -----------------------------------------------------------------
                //We need CustomDP to be able to call XmlReaderHelper.ReadXml
                //from the generated transformation class.
                // -----------------------------------------------------------------
                return new string[]
                {
                    "System.Xml",
                    "CustomDP"
                };
            }
        }
    
        // -------------------------------------------------------------------------
        // The code that we are adding to the generated transformation class
        // will call this method.
        // -------------------------------------------------------------------------
        public static class XmlReaderHelper
        {
            public static XmlDocument ReadXml(string fileName)
            {
                XmlDocument d = new XmlDocument();
    
                using (XmlReader reader = XmlReader.Create(fileName))
                {
                    try
                    {
                        d.Load(reader);
                    }
                    catch (System.Xml.XmlException e)
                    {
                        throw new DirectiveProcessorException("Unable to read the XML file.", e);
                    }
                }
                return d;
            }
        }
    }
    
  4. Yalnızca Visual Basic için Proje menüsünü açın ve ÖzelDP Özellikleri'ne tıklayın. Uygulama sekmesindeki Kök ad alanında varsayılan değeri olan öğesini CustomDPsilin.

  5. Dosya menüsünde Tümünü Kaydet’e tıklayın.

  6. Yapı menüsünde Yapı Çözümü’ne tıklayın.

Projeyi Oluşturma

Projeyi derleyin. Yapı menüsünde Yapı Çözümü’ne tıklayın.

Yönerge İşlemcisini Kaydetme

Visual Studio'daki bir metin şablonundan yönerge çağırabilmeniz için önce yönerge işlemcisi için bir kayıt defteri anahtarı eklemeniz gerekir.

Not

Yönerge işlemcisini birden fazla bilgisayara yüklemek istiyorsanız, derlemenizle birlikte .pkgdef dosyası içeren bir Visual Studio Uzantısı (VSIX) tanımlamak daha iyidir. Daha fazla bilgi için bkz . Özel Yönerge İşlemcisi Dağıtma.

Yönerge işlemcilerinin anahtarları, kayıt defterinde aşağıdaki konumda bulunur:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors

64-bit sistemler için kayıt defteri konumu şudur:

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors

Bu bölümde, özel bir yönerge işlemciniz için aynı konumda kayıt defterine bir anahtar eklersiniz.

Dikkat

Kayıt defterini yanlış düzenlemek sisteminize ciddi zararlar verebilir. Kayıt defterinde değişiklikler yapmadan önce, bilgisayarınızdaki tüm değerli verileri yedekleyin.

Yönerge işlemcisi için bir kayıt defteri anahtarı eklemek için

  1. regedit Başlat menüsü veya komut satırını kullanarak komutunu çalıştırın.

  2. HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors konumuna gidin ve düğüme tıklayın.

    64 bit sistemlerde HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\*.0\TextTemplating\DirectiveProcessors kullanın

  3. CustomDirectiveProcessor adlı yeni bir anahtar ekleyin.

    Not

    Bu, özel yönergelerinizin İşlemci alanına kullanacağınız addır. Bu adın, yönergenin adıyla, yönerge işlemcisi sınıfının adıyla veya yönerge işlemcisinin alan adıyla eşleşmesi gerekmez.

  4. Yeni dizenin adı olarak CustomDP.CustomDirectiveProcessor sahip, Sınıf adlı yeni bir dize değeri ekleyin.

  5. Bu kılavuzda daha önce oluşturduğunuz CustomDP.dll dosyasının yoluna eşit değere sahip, CodeBase adlı yeni bir dize değeri ekleyin.

    Örneğin, yol gibi C:\UserFiles\CustomDP\bin\Debug\CustomDP.dllgörünebilir.

    Kayıt defteri anahtarınız aşağıdaki değerlere sahip olmalı:

    Adı Tip Veri
    EnterprisePublishing REG_SZ (değer ayarlı değil)
    Sınıf REG_SZ CustomDP.CustomDirectiveProcessor
    CodeBase REG_SZ <Çözümünüzün>Yolu CustomDP\bin\Debug\CustomDP.dll

    Derlemeyi GAC içine koyduysanız, değerler aşağıdaki gibi görünmelidir:

    Adı Tip Veri
    EnterprisePublishing REG_SZ (değer ayarlı değil)
    Sınıf REG_SZ CustomDP.CustomDirectiveProcessor
    Derleme REG_SZ CustomDP.dll
  6. Visual Studio’yu yeniden başlatın.

Yönerge İşlemcisini Test Edin

Yönerge işlemcisini sınamak için onu çağıran bir metin şablonu yazmanız gerekir.

Bu örnekte, metin şablonu yönergeyi çağırır ve bir sınıf dosyasının belgelerini içeren bir XML dosyası adını geçirir. Metin şablonu, XML'de XmlDocument gezinmek ve belge açıklamalarını yazdırmak için yönergenin oluşturduğu özelliği kullanır.

Yönerge işlemcisini sınamada kullanmak için bir XML dosyası oluşturmak için

  1. Herhangi bir metin düzenleyicisini (örneğin, Not Defteri) kullanarak DocFile.xml adlı bir dosya oluşturun.

    Not

    Bu dosyayı herhangi bir konumda (örneğin, C:\Test\DocFile.xml) oluşturabilirsiniz.

  2. XML dosyasına aşağıdakileri ekleyin:

    <?xml version="1.0"?>
    <doc>
        <assembly>
            <name>xmlsample</name>
        </assembly>
        <members>
            <member name="T:SomeClass">
                <summary>Class level summary documentation goes here.</summary>
                <remarks>Longer comments can be associated with a type or member through the remarks tag</remarks>
            </member>
            <member name="F:SomeClass.m_Name">
                <summary>Store for the name property</summary>
            </member>
            <member name="M:SomeClass.#ctor">
                <summary>The class constructor.</summary>
            </member>
            <member name="M:SomeClass.SomeMethod(System.String)">
                <summary>Description for SomeMethod.</summary>
                <param name="s">Parameter description for s goes here</param>
                <seealso cref="T:System.String">You can use the cref attribute on any tag to reference a type or member and the compiler will check that the reference exists.</seealso>
            </member>
            <member name="M:SomeClass.SomeOtherMethod">
                <summary>Some other method.</summary>
                <returns>Return results are described through the returns tag.</returns>
                <seealso cref="M:SomeClass.SomeMethod(System.String)">Notice the use of the cref attribute to reference a specific method</seealso>
            </member>
            <member name="M:SomeClass.Main(System.String[])">
                <summary>The entry point for the application.</summary>
                <param name="args">A list of command line arguments</param>
            </member>
            <member name="P:SomeClass.Name">
                <summary>Name property</summary>
                <value>A value tag is used to describe the property value</value>
            </member>
        </members>
    </doc>
    
  3. Dosyayı kaydedip kapatın

Yönerge işlemcisini sınamak için bir metin şablonu oluşturmak için

  1. Visual Studio'da, TemplateTest adlı bir C# veya Visual Basic kitaplık projesi oluşturun.

  2. TestDP.tt adlı yeni bir metin şablonu dosyasını ekleyin.

  3. TestDP.tt Özel Araç özelliğinin olarak TextTemplatingFileGeneratorayarlandığından emin olun.

  4. TestDP.tt içeriğini aşağıdaki metin olarak değiştirin.

    Not

    dizesini <YOUR PATH> DocFile.xml dosyasının yoluyla değiştirin.

    Metin şablonunun dilinin yönerge işlemcisinin diliyle eşleşmesine gerek yoktur.

    <#@ assembly name="System.Xml" #>
    <#@ template debug="true" #>
    <#@ output extension=".txt" #>
    
    <#  // This will call the custom directive processor. #>
    <#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #>
    
    <#  // Uncomment this line if you want to see the generated transformation class. #>
    <#  // System.Diagnostics.Debugger.Break(); #>
    
    <#  // This will use the results of the directive processor. #>
    <#  // The directive processor has read the XML and stored it in Document0. #>
    <#
        XmlNode node = Document0.DocumentElement.SelectSingleNode("members");
    
        foreach (XmlNode member in node.ChildNodes)
        {
            XmlNode name = member.Attributes.GetNamedItem("name");
            WriteLine("{0,7}:  {1}", "Name", name.Value);
    
            foreach (XmlNode comment in member.ChildNodes)
            {
                WriteLine("{0,7}:  {1}", comment.Name, comment.InnerText);
            }
            WriteLine("");
        }
    #>
    
    <# // You can call the directive processor again and pass it a different file. #>
    <# // @ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\<Your Second File>" #>
    
    <#  // To use the results of the second directive call, use Document1. #>
    <#
        // XmlNode node2 = Document1.DocumentElement.SelectSingleNode("members");
    
        // ...
    #>
    

    Not

    Bu örnekte parametresinin Processor değeri şeklindedir CustomDirectiveProcessor. parametresinin Processor değeri, işlemcinin kayıt defteri anahtarının adıyla eşleşmelidir.

  5. Dosya menüsünde Tümünü Kaydet'i seçin.

Yönerge işlemcisini sınamak için

  1. Çözüm Gezgini'da TestDP.tt sağ tıklayın ve ardından Özel Aracı Çalıştır'a tıklayın.

    Visual Basic kullanıcıları için TestDP.txt varsayılan olarak Çözüm Gezgini görünmeyebilir. Projeye atanan tüm dosyaları görüntülemek için Proje menüsünü açın ve Tüm Dosyaları Göster'e tıklayın.

  2. Çözüm Gezgini'da TestDP.txt düğümünü genişletin ve ardından TestDP.txt dosyasına çift tıklayarak düzenleyicide açın.

    Oluşturulan metin çıktısı görüntülenir. Çıktı aşağıdaki gibi görünmelidir:

       Name:  T:SomeClass
    summary:  Class level summary documentation goes here.
    remarks:  Longer comments can be associated with a type or member through the remarks tag
    
       Name:  F:SomeClass.m_Name
    summary:  Store for the name property
    
       Name:  M:SomeClass.#ctor
    summary:  The class constructor.
    
       Name:  M:SomeClass.SomeMethod(System.String)
    summary:  Description for SomeMethod.
      param:  Parameter description for s goes here
    seealso:  You can use the cref attribute on any tag to reference a type or member and the compiler will check that the reference exists.
    
       Name:  M:SomeClass.SomeOtherMethod
    summary:  Some other method.
    returns:  Return results are described through the returns tag.
    seealso:  Notice the use of the cref attribute to reference a specific method
    
       Name:  M:SomeClass.Main(System.String[])
    summary:  The entry point for the application.
      param:  A list of command line arguments
    
       Name:  P:SomeClass.Name
    summary:  Name property
      value:  A value tag is used to describe the property value
    

Oluşturulan Metne HTML Ekleme

Özel yönerge işlemcinizi sınadıktan sonra, oluşturulan metninize HTML eklemek isteyebilirsiniz.

Oluşturulan metninize HTML eklemek için

  1. TestDP.tt içindeki kodu aşağıdakilerle değiştirin. HTML vurgulanır. Dizeyi YOUR PATH DocFile.xml dosyasının yoluyla değiştirdiğinden emin olun.

    Not

    Ek open <# ve close #> etiketleri, deyim kodunu HTML etiketlerinden ayırır.

    <#@ assembly name="System.Xml" #>
    <#@ template debug="true" #>
    <#@ output extension=".htm" #>
    
    <#  // This will call the custom directive processor #>
    <#@ CoolDirective Processor="CustomDirectiveProcessor" FileName="<YOUR PATH>\DocFile.xml" #>
    
    <#  // Uncomment this line if you want to see the generated transformation class #>
    <#  // System.Diagnostics.Debugger.Break(); #>
    
    <html><body>
    
    <#  // This will use the results of the directive processor #>.
    <#  // The directive processor has read the XML and stored it in Document0#>.
    <#
        XmlNode node = Document0.DocumentElement.SelectSingleNode("members");
    
        foreach (XmlNode member in node.ChildNodes)
        {
    #>
    <h3>
    <#
            XmlNode name = member.Attributes.GetNamedItem("name");
            WriteLine("{0,7}:  {1}", "Name", name.Value);
    #>
    </h3>
    <#
            foreach (XmlNode comment in member.ChildNodes)
            {
                WriteLine("{0,7}:  {1}", comment.Name, comment.InnerText);
    #>
    <br/>
    <#
            }
        }
    #>
    </body></html>
    
  2. Dosya menüsünde TestDP.txt Dosyasını Kaydet'e tıklayın.

  3. Çıktıyı tarayıcıda görüntülemek için, Çözüm Gezgini TestDP.htm'ye sağ tıklayın ve Tarayıcıda Görüntüle'ye tıklayın.

    Çıkışınız, HTML biçiminin uygulanmış olması dışında özgün metinle aynı olmalıdır. Her öğe adı kalın olarak görünür.