首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

實作自訂 VsSingleFileGenerator:XML 模型自動產生 C# 實體代碼

     編輯:關於.NET

Visual Studio 的單一檔案產生器(Single File Generator)可在儲存特定副檔名檔案時,自動產生附屬代碼檔(如 .designer.cs)。本文示範實作 IVsSingleFileGenerator,將自訂 XML 模型轉換為 C# 實體類別與映射代碼。

目標:XML 模型 → C# 代碼

輸入 XML (NClay 模型描述)

<?xml version="1.0" encoding="utf-8"?>
<nclay_models xmlns="https://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>

輸出 C# 代碼

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; } }
}

核心實作:ModelGenerator 類別

實作 IVsSingleFileGenerator 與 IObjectWithSite:

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

    public CodeDomProvider CodeProvider {
        get { return codeProvider ?? (codeProvider = CodeDomProvider.CreateProvider("C#")); }
        set { codeProvider = value ?? throw new ArgumentNullException(); }
    }

    #region IVsSingleFileGenerator
    public int DefaultExtension(out string ext) {
        ext = string.Empty;
        var defExt = CodeProvider.FileExtension;
        if (!string.IsNullOrEmpty(defExt) && defExt[0] != '.') defExt = "." + defExt;
        if (!string.IsNullOrEmpty(defExt)) ext = ".NClay" + 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(nameof(bstrInputFileContents));
        
        codeFilePath = wszInputFilePath;
        codeFileNameSpace = wszDefaultNamespace;
        codeGeneratorProgress = pGenerateProgress;

        byte[] generatedStuff = 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; // S_OK
    }
    #endregion

    #region IObjectWithSite
    public void GetSite(ref Guid riid, out IntPtr ppvSite) { /* COM 站點查詢 */ }
    public void SetSite(object pUnkSite) { site = pUnkSite; codeProvider = null; }
    #endregion

    #region 私有方法
    protected byte[] GenerateCode(string inputFileName, string inputFileContent) {
        var compileUnit = CodeGenerator.GeneratorByCodeDom(
            new XmlDocument { InnerXml = inputFileContent }, CodeProvider);
        
        codeGeneratorProgress?.Progress(0x4b, 100);
        
        using (var ms = new MemoryStream())
        using (var writer = new StreamWriter(ms, Encoding.UTF8)) {
            CodeProvider.GenerateCodeFromCompileUnit(compileUnit, writer, null);
            writer.Flush();
            codeGeneratorProgress?.Progress(100, 100);
            return StreamToBytes(ms);
        }
    }

    protected byte[] StreamToBytes(Stream stream) { /* 流轉位元組陣列 */ }
    private void ThrowOnFailure(int hr) { if (hr < 0) Marshal.ThrowExceptionForHR(hr); }
    #endregion
}

CodeDom 動態產生代碼邏輯

CodeGenerator.GeneratorByCodeDom 解析 XML,建構 CodeCompileUnit:

  • 命名空間、匯入
  • 每個 <class> 產生:
    • 實體類別(Partial、TableMapper 屬性)
    • 欄位屬性(PrimaryKey/Column 屬性、私有欄位、get/set)
    • 巢狀 Mapper 類別(繼承 Table、ObjectField 屬性、建構子初始化)
  • ModelContext 靜態屬性提供 Mapper 存取點

型別對應邏輯(Mapper 欄位型別):

switch(type) {
    case "System.Int16": case "System.Int32": ... 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;
}

註冊部署

  1. 將組件安裝至 GAC(gacutil /i NClay.Generators.dll)
  2. 註冊表寫入(VS 2008 / 9.0 範例):
[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
// 同樣註冊至 {E6FDF8B0-F3D1-11D4-8576-0002A516ECE8} (VB) 與 {FAE04EC1-301F-11d3-BF4B-00C04F79EFBC} (C#)

使用方式

  1. 方案總管右鍵 XML 檔 → 屬性
  2. 「自訂工具」欄位填入:NClayGenerator
  3. 儲存 XML 即自動產生/更新附屬 .NClay.cs 檔

進階:XML Schema 驗證與 IntelliSense

編寫 .xsd 結構描述並關聯 XML,即可獲得驗證與智慧提示:

XML Schema 驗證示意圖

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved