程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 實現自定義的VsSingleFileGenerator

實現自定義的VsSingleFileGenerator

編輯:關於.NET

用過VS.NET的朋友應該會發現,在編輯一些文件時VS會在文件下面自動創建它的附屬文件.而這些附屬 文件往往是根據設計文件生成的代碼文件來的.對於我們想實現這樣的功能怎辦呢?其實MS早就為我們想好 了,只要簡單地實現IVsSingleFileGenerator;說是簡單不過還是要做些功夫的,就是把編寫後 VsSingleFileGenerator注冊到共公程序集中,然後在注冊表裡添加一些東西才行.下面介紹自己實現NClay 實體設計的SingleFileGenerator,有需要的朋友可以參考代碼實現自己的SingleFileGenerator:)

實現目的編寫XML模型描述後自動生成附屬C#代碼文件.

XML設計文件:

<?xml version="1.0" encoding="utf-8" ?>

<nclay_models xmlns="http://nclay.cn/model.xsd" namespace="Blogs.Entities">

  <class name="User" table="TUser" comment="">

    <id name="UserID" type="System.String"/>

    <property name="UserName" type="System.String"/>

    <property name="UserPWD" type="System.String"/>

    <property name="EMail" type="System.String"/>

    <property name="Enabled" type="System.String"/>

    <property name ="Remark" type="System.String"/>

  </class>

</nclay_models>

生成代碼模型文件內容:

    using System;
    using System.Data;
    using NClay.Data;
    using NClay.Data.Mappings;

    [TableMapper(Name="User")]
    public partial class User {

        private void mUserID;

        [PrimaryKey(Name="UserID")]
        public virtual void UserID {
            get {
                return this.mUserID;
            }
            set {
                this.mUserID = value;
            }
        }

        public partial class Mapper : Table {

            private ObjectField mAll;

            private ObjectField mUserID;

            public Mapper() :
                    base("User") {
                this.mAll = new ObjectField("*", this);
                this.mUserID = new ObjectField("UserID", this);
            }

            public virtual ObjectField All {
                get {
                    return this.mAll;
                }
            }

            public virtual ObjectField UserID {
                get {
                    return this.mUserID;
                }
            }
        }
    }

    public partial class ModelContext {

        static User.Mapper mUser = new User.Mapper();

        public static User.Mapper User {
            get {
                return mUser;
            }
        }
    }

對於SingleFileGenerator的編寫我直接就貼代碼,其實也沒什麼好講就一個類.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell.Interop;
using System.ComponentModel;
using System.CodeDom.Compiler;
using Microsoft.VisualStudio.Shell;
using VSOLE = Microsoft.VisualStudio.OLE.Interop;
using System.CodeDom;
using System.IO;
using System.Xml;

namespace NClay.Generators
{

    [Guid("2F6150C6-BC48-4733-96FE-91F2A90AADCF")]
    public class ModelGenerator : IVsSingleFileGenerator, VSOLE::IObjectWithSite
    {
        private CodeDomProvider codeProvider;

        private string codeFileNameSpace;
        private string codeFilePath;

        private object site;

        private IVsGeneratorProgress codeGeneratorProgress;

        public CodeDomProvider CodeProvider
        {
            get
            {
                if (this.codeProvider == null)
                {
                    codeProvider = CodeDomProvider.CreateProvider ("C#");
                }

                return this.codeProvider;
            }

            set
            {
                if (value == null)
                {
                    throw new ArgumentNullException();
                }

                this.codeProvider = value;
            }
        }

        IVsSingleFileGenerator Members#region IVsSingleFileGenerator Members

        public int DefaultExtension(out string ext)
        {
            string defExt;
            ext = string.Empty;

            defExt = this.CodeProvider.FileExtension;

            if (((defExt != null) && (defExt.Length > 0)) && (defExt[0] != '.'))
            {
                defExt = "." + defExt;
            }

            if (!string.IsNullOrEmpty(defExt))
            {
                ext = ".NCaly" + defExt;
            }

            return 0;
        }

        public int Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] pbstrOutputFileContents, out uint pbstrOutputFileContentSize, IVsGeneratorProgress pGenerateProgress)
        {
            if (bstrInputFileContents == null)
            {
                throw new ArgumentNullException(bstrInputFileContents);
            }

            this.codeFilePath = wszInputFilePath;
            this.codeFileNameSpace = wszDefaultNamespace;
            this.codeGeneratorProgress = pGenerateProgress;

            byte[] generatedStuff = this.GenerateCode(wszInputFilePath, bstrInputFileContents);

            if (generatedStuff == null)
            {
                pbstrOutputFileContents[0] = IntPtr.Zero;
                pbstrOutputFileContentSize = 0;
            }
            else
            {
                pbstrOutputFileContents[0] = Marshal.AllocCoTaskMem (generatedStuff.Length);
                Marshal.Copy(generatedStuff, 0, pbstrOutputFileContents[0], generatedStuff.Length);
                pbstrOutputFileContentSize = (uint) generatedStuff.Length;
            }
            return 0;
        }
        #endregion

        IObjectWithSite Members#region IObjectWithSite Members

        public void GetSite(ref Guid riid, out IntPtr ppvSite)
        {
            if (this.site == null)
            {
                throw new Win32Exception(-2147467259);
            }

            IntPtr objectPointer = Marshal.GetIUnknownForObject (this.site);

            try
            {
                Marshal.QueryInterface(objectPointer, ref riid, out ppvSite);
                if (ppvSite == IntPtr.Zero)
                {
                    throw new Win32Exception(-2147467262);
                }
            }
            finally
            {
                if (objectPointer != IntPtr.Zero)
                {
                    Marshal.Release(objectPointer);
                    objectPointer = IntPtr.Zero;
                }
            }
        }

        public void SetSite(object pUnkSite)
        {
            this.site = pUnkSite;
            this.codeProvider = null;
        }

        #endregion

        Private Methods#region Private Methods
        protected byte[] GenerateCode(string inputFileName, string inputFileContent)
        {
            CodeCompileUnit compileUnit;

            StreamWriter writer = new StreamWriter(new MemoryStream(), Encoding.UTF8);

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(inputFileContent);

            //compileUnit = ClassGenerator.Create(doc, this.codeProvider);
            //補上代碼
            compileUnit = CodeGenerator.GeneratorByCodeDom(doc, this.CodeProvider);

            if (this.codeGeneratorProgress != null)
            {
                this.codeGeneratorProgress.Progress(0x4b, 100);
            }

            this.CodeProvider.GenerateCodeFromCompileUnit(compileUnit, writer, null);

            if (this.codeGeneratorProgress != null)
            {
                this.ThrowOnFailure(this.codeGeneratorProgress.Progress (100, 100));
            }
            writer.Flush();

            return this.StreamToBytes(writer.BaseStream);
        }

        protected byte[] StreamToBytes(Stream stream)
        {
            if (stream.Length == 0)
            {
                return new byte[0];
            }

            long pos = stream.Position;

            stream.Position = 0;

            byte[] buffer = new byte[(int)stream.Length];
            stream.Read(buffer, 0, buffer.Length);

            stream.Position = pos;
            return buffer;
        }

        private void ThrowOnFailure(int hr)
        {
            if ((hr < 0))
            {
                Marshal.ThrowExceptionForHR(hr);
            }
        }
        #endregion
    }

    class CodeGenerator
    {
        public static CodeCompileUnit GeneratorByCodeDom(XmlDocument doc, CodeDomProvider codeprovider)
        {
            CodeCompileUnit codeunit = new CodeCompileUnit();
            CodeNamespace nspace = null;
            try
            {
                nspace = new CodeNamespace(
                     doc.ChildNodes[1].Attributes ["namespace"].Value);
                codeunit.Namespaces.Add(nspace);
                nspace.Imports.Add(new CodeNamespaceImport("System"));
                nspace.Imports.Add(new CodeNamespaceImport ("System.Data"));
                nspace.Imports.Add(new CodeNamespaceImport ("NClay.Data"));
                nspace.Imports.Add(new CodeNamespaceImport ("NClay.Data.Mappings"));
                foreach (XmlNode cls in doc.ChildNodes[1].ChildNodes)
                {
                    if (cls.Name.ToLower() == "class")
                        CreateClass(nspace, cls);
                }

            }
            catch (Exception e_)
            {
                nspace.Comments.Add(new CodeCommentStatement (e_.Message));
            }
            return codeunit;
        }
        private static void CreateClass(CodeNamespace nspace, XmlNode cls)
        {
            CodeMemberField mfield;
            CodeMemberProperty mproperty;
            string type, name, column, table, sequence, cast, comment;
            comment = null;
            sequence = null;
            cast = null;
            name = cls.Attributes["name"].Value;
            if (cls.Attributes["table"] == null)
                table = name;
            else
                table = cls.Attributes["table"].Value;
            if (cls.Attributes["comment"] != null)
                comment = cls.Attributes["comment"].Value;
            name = FirstToUpper(name);
            table = FirstToUpper(table);
            CodeTypeDeclaration entity = new CodeTypeDeclaration(
                name);
            entity.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("TableMapper"),
                new CodeAttributeArgument("Name", new CodePrimitiveExpression(table))));
            entity.IsPartial = true;
            if (comment != null)
            {
                entity.Comments.Add(new CodeCommentStatement ("<summary>", true));
                entity.Comments.Add(new CodeCommentStatement(comment, true));
                entity.Comments.Add(new CodeCommentStatement ("</summary>", true));
            }
            CodeTypeDeclaration mapper = new CodeTypeDeclaration("Mapper");
            mapper.IsPartial = true;
            mapper.BaseTypes.Add(new CodeTypeReference("Table"));
            entity.Members.Add(mapper);
            if (comment != null)
            {
                mapper.Comments.Add(new CodeCommentStatement ("<summary>", true));
                mapper.Comments.Add(new CodeCommentStatement(comment, true));
                mapper.Comments.Add(new CodeCommentStatement ("</summary>", true));
            }
            CodeConstructor constructor = new CodeConstructor();
            constructor.Attributes = MemberAttributes.Public;
            constructor.BaseConstructorArgs.Add(new CodePrimitiveExpression (table));
            mapper.Members.Add(constructor);
            mfield = new CodeMemberField(new CodeTypeReference("ObjectField"), "mAll");
            mapper.Members.Add(mfield);
            mproperty = new CodeMemberProperty();
            mproperty.Type = new CodeTypeReference("ObjectField");
            mproperty.Name = "All";
            mproperty.Attributes = MemberAttributes.Public;
            mproperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "mAll")));
            mapper.Members.Add(mproperty);
            CodeAssignStatement assingexp;
            assingexp = new CodeAssignStatement(
                new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "mAll"),
                new CodeObjectCreateExpression("ObjectField",
                    new CodePrimitiveExpression("*"),
                    new CodeThisReferenceExpression()));
            constructor.Statements.Add(assingexp);

            comment = null;
            foreach (XmlNode property in cls.ChildNodes)
            {
                entity#region entity

                type = property.Attributes["type"].Value;
                name = property.Attributes["name"].Value;
                name = FirstToUpper(name);
                if (property.Attributes["sequence"] != null)
                    sequence = property.Attributes ["sequence"].Value;
                if (property.Attributes["cast"] != null)
                    cast = property.Attributes["cast"].Value;
                if (property.Attributes["column"] == null)
                    column = name;
                else
                    column = property.Attributes["column"].Value;
                if (property.Attributes["comment"] != null)
                    comment = property.Attributes["comment"].Value;
                column = FirstToUpper(column);
                mfield = new CodeMemberField(type, "m" + name);
                entity.Members.Add(mfield);
                mproperty = new CodeMemberProperty();

                mproperty.Type = new CodeTypeReference(type);
                mproperty.Name = name;
                mproperty.Attributes = MemberAttributes.Public;
                if (comment != null)
                {
                    mproperty.Comments.Add(new CodeCommentStatement ("<summary>", true));
                    mproperty.Comments.Add(new CodeCommentStatement (comment, true));
                    mproperty.Comments.Add(new CodeCommentStatement ("</summary>", true));
                }
                mproperty.GetStatements.Add(new CodeMethodReturnStatement (new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "m" + name)));
                mproperty.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "m" + name), new CodePropertySetValueReferenceExpression()));
                entity.Members.Add(mproperty);
                if (property.Name == "id")
                {
                    if (sequence == null)
                    {

                        mproperty.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("PrimaryKey"),
                    new CodeAttributeArgument("Name", new CodePrimitiveExpression(column))));
                    }
                    else
                    {
                        mproperty.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("PrimaryKey"),
                    new CodeAttributeArgument("Name", new CodePrimitiveExpression(column)),
                    new CodeAttributeArgument("Sequence", new CodeTypeOfExpression(sequence))));
                    }
                }
                else
                {
                    if (cast == null)
                    {
                        mproperty.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("Column"),
                       new CodeAttributeArgument("Name", new CodePrimitiveExpression(column))));
                    }
                    else
                    {
                        mproperty.CustomAttributes.Add(new CodeAttributeDeclaration(new CodeTypeReference("Column"),
                       new CodeAttributeArgument("Name", new CodePrimitiveExpression(column)),
                       new CodeAttributeArgument("Cast", new CodeTypeOfExpression(cast))));
                    }

                }

                #endregion
                string mapptype;
                mapper#region mapper
                switch(type)
                {
                    case "System.Int16":
                    case "System.Int32":
                    case "System.Int64":
                    case"System.Double":
                    case "System.Single":
                    case "System.Decimal":
                    case "System.Byte":
                        mapptype = "NumberField";
                        break;
                    case"System.Byte[]":
                        mapptype = "BytesField";
                        break;
                    case "System.Boolean":
                        mapptype = "BooleanField";
                        break;
                    case"System.DateTime":
                        mapptype = "DateTimeField";
                        break;
                    case "System.Guid":
                        mapptype = "GuidField";
                        break;
                    case"System.String":
                        mapptype = "StringField";
                        break;
                    default:
                        mapptype = "ObjectField";
                        break;
                }

                mfield = new CodeMemberField(new CodeTypeReference (mapptype), "m" + name);

                mapper.Members.Add(mfield);
                mproperty = new CodeMemberProperty();
                mproperty.Type = new CodeTypeReference(mapptype);
                mproperty.Name = name;
                mproperty.Attributes = MemberAttributes.Public;
                if (comment != null)
                {
                    mproperty.Comments.Add(new CodeCommentStatement ("<summary>", true));
                    mproperty.Comments.Add(new CodeCommentStatement (comment, true));
                    mproperty.Comments.Add(new CodeCommentStatement ("</summary>", true));
                }
                mproperty.GetStatements.Add(new CodeMethodReturnStatement (new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "m" + name)));
                mapper.Members.Add(mproperty);

                assingexp = new CodeAssignStatement(
                    new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "m" + name),
                    new CodeObjectCreateExpression(mapptype,
                        new CodePrimitiveExpression(column),
                        new CodeThisReferenceExpression()));
                constructor.Statements.Add(assingexp);
                #endregion

            }

            nspace.Types.Add(entity);
            name = cls.Attributes["name"].Value;
            name = FirstToUpper(name);
            CodeTypeDeclaration modelcontext = new CodeTypeDeclaration ("ModelContext");
            modelcontext.IsPartial = true;
            mfield = new CodeMemberField(new CodeTypeReference(name + ".Mapper"), "m" + name);
            mfield.Attributes = MemberAttributes.Static;
            mfield.InitExpression = new CodeObjectCreateExpression(new CodeTypeReference(name + ".Mapper"), new CodeExpression[] { });
            modelcontext.Members.Add(mfield);
            mproperty = new CodeMemberProperty();
            mproperty.Type = new CodeTypeReference(name + ".Mapper");
            mproperty.Name = name;
            mproperty.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            mproperty.GetStatements.Add(new CodeMethodReturnStatement(new CodeVariableReferenceExpression("m" + name)));
            modelcontext.Members.Add(mproperty);

            nspace.Types.Add(modelcontext);
        }
        private static string FirstToUpper(string value)
        {
            string first = value.Substring(0, 1).ToUpper();
            return first + value.Substring(1, value.Length - 1);
        }

    }
}

接下來就是注冊

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\CLSID\{2F6150C6-BC48-4733- 96FE-91F2A90AADCF}]
@="NClay.Generators.ModelGenerator"
"InprocServer32"="d:\\windows\\system32\\mscoree.dll"
"Class"="NClay.Generators.ModelGenerator"
"Assembly"="NClay.Generators, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8c768ba656ce9125"
"ThreadingModel"="Both"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{164B10B9-B200-11D0- 8C61-00A0C91E29D5}\NClayGenerator]
@="NClay Model Generator"
"CLSID"="{2F6150C6-BC48-4733-96FE-91F2A90AADCF}"
"GeneratesDesignTimeSource"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{E6FDF8B0-F3D1-11D4- 8576-0002A516ECE8}\NClayGenerator]
@="NClay Model Generator"
"CLSID"="{2F6150C6-BC48-4733-96FE-91F2A90AADCF}"
"GeneratesDesignTimeSource"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{FAE04EC1-301F-11d3- BF4B-00C04F79EFBC}\NClayGenerator]
@="NClay Model Generator"
"CLSID"="{2F6150C6-BC48-4733-96FE-91F2A90AADCF}"
"GeneratesDesignTimeSource"=dword:00000001

所有CLSID對應的值是實現IVsSingleFileGenerator的Guid描述.(記住要所DLL注冊到全局程序集中)

這樣SingleFileGenerator就完成了,在VS中使用這個SingleFileGenerator.在解決方案管理器右鍵文 件屬性,在自定義工具填寫上: NclayGenerator。

在這個應用中也許有朋友想這樣編寫XML很麻煩,又沒有編寫提示和驗證等功能.其實MS也為我們想好了 ,只需要寫個XmlSchema就可以了.下載代碼裡也有考參文件.

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved