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

.NET 專案二次開發解決方案:動態編譯技術深度應用

     編輯:關於.NET

背景與動機

傳統二次開發多仰賴 SQL 腳本直接操作資料庫,雖簡單但缺點明顯:需深諳資料庫結構、易出錯、無法複用業務邏輯層代碼。本文探討利用 .NET 動態編譯 技術,將客製化邏輯編譯為組件並動態載入執行,實現靈活、安全、可維護的二次開發框架。

動態編譯基礎入門

.NET 提供 System.CodeDom.Compiler 命名空間,支援在運行時編譯 C#/VB 代碼。核心類別:

  • CSharpCodeProvider:C# 代碼提供者
  • CompilerParameters:編譯參數(參考組件、輸出路徑、優化選項等)
  • CompilerResults:編譯結果,含錯誤資訊與編譯後組件

最簡範例:字串代碼編譯執行

using System;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.Reflection;

CodeDomProvider provider = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("System.dll");

string code = @"
using System;
public class MyClass {
    public void Hello() {
        Console.WriteLine(""Hello Dynamic!"");
    }
}";

CompilerResults cr = provider.CompileAssemblyFromSource(cp, code);
if (cr.Errors.HasErrors) { /* 處理錯誤 */ }
else {
    Assembly asm = cr.CompiledAssembly;
    Type t = asm.GetType("MyClass");
    object obj = asm.CreateInstance("MyClass");
    MethodInfo mi = t.GetMethod("Hello");
    mi.Invoke(obj, null); // 輸出: Hello Dynamic!
}

進階:結構化代碼構建與 CodeDOM

直接拼接字串難以維護。使用 System.CodeDom 以物件模型構建代碼結構,再由 CodeGenerator 產生格式良好的原始碼。

代碼構建器核心邏輯

public static CodeCompileUnit CreateExecutionClass(string ns, string className, string methodBody)
{
    CodeCompileUnit ccu = new CodeCompileUnit();
    CodeNamespace cns = new CodeNamespace(ns);
    cns.Imports.Add(new CodeNamespaceImport("System"));
    ccu.Namespaces.Add(cns);

    CodeTypeDeclaration clazz = new CodeTypeDeclaration(className);
    cns.Types.Add(clazz);

    CodeMemberMethod method = new CodeMemberMethod();
    method.Name = "Execute";
    method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
    method.ReturnType = new CodeTypeReference(typeof(string));
    method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), "input"));
    method.Statements.Add(new CodeSnippetStatement(methodBody));

    clazz.Members.Add(method);
    return ccu;
}

實戰架構:可配置的動態業務引擎

針對「薪資計算演算法因客戶而異」等場景,設計資料庫驅動的動態編譯框架。

1. 資料模型:儲存函數腳本與參數

資料表結構

關鍵欄位:ScriptBody(方法本體)、Parameters(XML 格式參數定義)、ReturnType、ClassName、ClassNameSpace。

2. 參數解析:XML 轉 CodeDom 參數集合

public List GetParameterList()
{
    var list = new List();
    if (this.Parameters != null)
    {
        foreach (var p in this.Parameters.Elements("Parameter"))
        {
            Type type = Type.GetType(p.Element("Type").Value);
            string name = p.Element("ParameterName").Value;
            list.Add(new CodeParameterDeclarationExpression(type, name));
        }
    }
    return list;
}

3. 代碼構建器:組裝完整類別

public class CodeBuilder
{
    private List scripts;
    private string ns, className;

    public CodeBuilder(List scripts) { /* 初始化 */ }

    public string GenerateCode()
    {
        CodeCompileUnit ccu = new CodeCompileUnit();
        CodeNamespace cns = new CodeNamespace(ns);
        cns.Imports.Add(new CodeNamespaceImport("System"));
        ccu.Namespaces.Add(cns);

        CodeTypeDeclaration clazz = new CodeTypeDeclaration(className);
        cns.Types.Add(clazz);

        foreach (var script in scripts)
        {
            CodeMemberMethod method = new CodeMemberMethod();
            method.Name = script.FunctionName;
            method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            foreach (var p in script.GetParameterList()) method.Parameters.Add(p);
            if (!string.IsNullOrEmpty(script.ReturnType))
                method.ReturnType = new CodeTypeReference(Type.GetType(script.ReturnType));
            method.Statements.Add(new CodeSnippetStatement(script.ScriptBody));
            clazz.Members.Add(method);
        }

        // 產生原始碼字串
        CSharpCodeProvider provider = new CSharpCodeProvider();
        CodeGeneratorOptions opts = new CodeGeneratorOptions { BracingStyle = "C", IndentString = "\t" };
        using (StringWriter sw = new StringWriter())
        {
            provider.GenerateCodeFromCompileUnit(ccu, sw, opts);
            return sw.ToString();
        }
    }
}

4. 編譯器與執行器

public class DynamicCompiler
{
    private StringCollection refs = new StringCollection { "mscorlib.dll", "System.dll" };

    public void Compile(string code, string outputDll)
    {
        CompilerParameters cp = new CompilerParameters();
        cp.CompilerOptions = "/target:library /optimize";
        cp.OutputAssembly = outputDll;
        cp.ReferencedAssemblies.AddRange(refs);

        CSharpCodeProvider provider = new CSharpCodeProvider();
        CompilerResults cr = provider.CompileAssemblyFromSource(cp, code);
        if (cr.Errors.HasErrors) throw new Exception(/* 錯誤訊息 */);
    }
}

public class Executor
{
    public object Execute(string dllPath, string typeName, string methodName, params object[] args)
    {
        Assembly asm = Assembly.LoadFrom(dllPath);
        Type t = asm.GetType(typeName);
        object instance = asm.CreateInstance(typeName);
        MethodInfo mi = t.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
        return mi.Invoke(instance, args);
    }
}

5. 主程式整合測試

var f1 = new FunctionScript {
    ClassName = "MyClass", ClassNameSpace = "My", FunctionName = "Calc",
    ReturnType = "System.Decimal",
    Parameters = XElement.Parse(@"System.Decimalamount"),
    ScriptBody = "\t\t\treturn amount * 1.05m;" // 含稅計算
};

var builder = new CodeBuilder(new List { f1 });
string code = builder.GenerateCode();
Console.WriteLine(code); // 查看產生的代碼

new DynamicCompiler().Compile(code, "BizLogic.dll");
object result = new Executor().Execute("BizLogic.dll", "My.MyClass", "Calc", 1000m);
Console.WriteLine("計算結果: " + result); // 1050
執行結果

架構時序圖

時序圖

優缺點分析與最佳實務

優點缺點/風險
業務邏輯外部化,無需重新部署主程式動態編譯有效能開銷,不宜高頻呼叫
可複用現有 .NET 類庫、LINQ、EF 等生態安全性:需嚴格驗證腳本來源,防止惡意代碼注入
支援版本控管、測試、除錯(PDB)除錯較靜態代碼困難,建議記錄編譯錯誤日誌
參數強型別,編譯期即可發現錯誤部署需確保目標機器安裝 .NET SDK 或 Build Tools

總結

動態編譯為 .NET 二次開發提供了強大而優雅的解決方案。結合 CodeDOM 結構化代碼生成、資料庫腳本管理、獨立編譯/執行組件,可構建出「業務邏輯即配置」的靈活架構。實務上建議搭配快取機制(如依腳本雜湊快取組件)、沙箱隔離(AppDomain/AppContainer)與完整的審計日誌,確保生產環境穩定安全。

完整原始碼下載:源碼下載連結

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