实现自定义的VsSingleFileGenerator

          用过 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(0x4b100);
            }


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

            
if (this.codeGeneratorProgress != null)
            
{
                
this.ThrowOnFailure(this.codeGeneratorProgress.Progress(100100));
            }

            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(01).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对应的值是实现IVsSingleFileGeneratorGuid描述.(记住要所DLL注册到全局程序集中)

这样SingleFileGenerator就完成了,VS中使用这个SingleFileGenerator.在解决方案管理器右键文件属性,在自定义工具填写上: NclayGenerator

在这个应用中也许有朋友想这样编写XML很麻烦,又没有编写提示和验证等功能.其实MS也为我们想好了,只需要写个XmlSchema就可以了.下载代码里也有考参文件.

 

 

下载代码

参考资料:

其实在google 搜一下IVsSingleFileGenerator就有一堆资料

多谢timiil告诉我IVsSingleFileGenerator的功能.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值