程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .net項目的二次開發解決方案

.net項目的二次開發解決方案

編輯:關於.NET

公司原來項目的二次開發方式主要使用SQL,基本上也能滿足客戶的要求,優點是使用簡單,只要熟悉SQL語句就可以操作,缺點是受限制太多,需要對數據庫底層相當的了解,使用時容易出錯,無法直接調用業務層代碼等,研究了一下.net的動態編譯,感覺用它來做二次開發效果應該不錯的。

首先我們先做個demo來解釋一下動態編譯,下面這段代碼的意思就是先組織一個源碼字符串,然後編譯執行。

動態編譯簡單代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
namespace ConsoleApplication6
{
class Program
{
//C#代碼提供者
private static CodeDomProvider comp = new CSharpCodeProvider();
//用於調用編譯器的參數
private static CompilerParameters cp = new CompilerParameters();
private static MethodInfo mi;
static void Main(string[] args)
{
StringBuilder codeBuilder = new StringBuilder();
codeBuilder.AppendLine("using System;");
codeBuilder.AppendLine("public class MyClass");
codeBuilder.AppendLine("{");
codeBuilder.AppendLine("public void hello()");
codeBuilder.AppendLine("{");
codeBuilder.AppendLine("Console.WriteLine( \"hello\");");
codeBuilder.AppendLine("}");
codeBuilder.AppendLine("}");
//加入需要引用的程序集
cp.ReferencedAssemblies.Add("System.dll");
CompilerResults cr = comp.CompileAssemblyFromSource(cp, codeBuilder.ToString());
//如果有編譯錯誤
if (cr.Errors.HasErrors)
{
foreach (CompilerError item in cr.Errors)
{
Console.WriteLine(item.ToString());
}
}
else
{
Assembly a = cr.CompiledAssembly; //獲取已編譯的程序集
Type t = a.GetType("MyClass"); //利用反射獲得類型
object mode = a.CreateInstance("MyClass");
mi = t.GetMethod("hello", BindingFlags.Instance | BindingFlags.Public);
mi.Invoke(mode, new object[0]); //執行方法
}
}
}
}

了解了上面這段代碼,我們基本對動態編譯的概念清楚了,但是如果在項目中這樣使用的話,我們要自己去控制源代碼的全部文檔內容,如果大規模應用的話會非常的麻煩,需要重復編寫命名空間構造,類構造,函數構造等,如果我們還想看到一個格式良好的源碼,我們還必須自己控制格式。現在我們來介紹一種源碼構造方式來解決這些問題,請看如下代碼:

動態編譯簡單代碼改進

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.CodeDom;
using System.IO;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(GenerateCode("Hello","Hello","\t\t\treturn \"hello\";"));
}
public static CodeCompileUnit CreateExecutionClass(string typeNamespace,
string typeName,string scriptBody)
{
// 創建CodeCompileUnit以包含代碼
CodeCompileUnit ccu = new CodeCompileUnit();
// 分配需要的命名空間
CodeNamespace cns = new CodeNamespace(typeNamespace);
cns.Imports.Add(new CodeNamespaceImport("System"));
ccu.Namespaces.Add(cns);
// 創建新的類聲明
CodeTypeDeclaration parentClass = new CodeTypeDeclaration(typeName);
cns.Types.Add(parentClass);
// 創建獲得一個參數並返回一個字符串的Hello方法
CodeMemberMethod method = new CodeMemberMethod();
method.Name = "Hello";
method.Attributes = MemberAttributes.Public;
CodeParameterDeclarationExpression arg = new CodeParameterDeclarationExpression(typeof(string), "inputMessage");
method.Parameters.Add(arg);
method.ReturnType = new CodeTypeReference(typeof(string));
// 添加方法實體需要的代碼
CodeSnippetStatement methodBody = new CodeSnippetStatement(scriptBody);
method.Statements.Add(methodBody);
parentClass.Members.Add(method);
return ccu;
}
public static string GenerateCode(string typeNamespace,
string typeName,string scriptBody)
{
// 調用我們前面的方法創建CodeCompileUnit
CodeCompileUnit ccu = CreateExecutionClass(typeNamespace,
typeName, scriptBody); CSharpCodeProvider provider = new CSharpCodeProvider();
CodeGeneratorOptions options = new CodeGeneratorOptions();
options.BlankLinesBetweenMembers = false;
options.IndentString = "\t";//指定縮進字符
options.BracingStyle = "C";//大括號換行
StringWriter sw = new StringWriter();
try
{
provider.GenerateCodeFromCompileUnit(ccu, sw, options);
sw.Flush();
}
finally
{
sw.Close();
}
return sw.GetStringBuilder().ToString();
}

}
}

執行結果如下:

大家了解了動態編譯之後,我們這裡介紹一個稍微復雜一點的應用:

需求:我們先預定義一個執行流程,而具體執行代碼可以在我們項目部署之後再編寫。比如說工資的計算在不同應用中算法會有很大的不同。

我們首先定義一個數據庫中的數據結構:

然後將這個表拖入到dbml(生成的dbml文件請下載源碼)文件中,現在我們就開始編寫相應的代碼吧:

首先我們先來處理函數的參數,如果我們只是將參數列表的字符串存入數據庫中的話,我們還要根據格式序列化和反序列化這個參數,所以我們使用xml存入sqlserver2005中,格式如下

參數格式

<Parameters>
 <Parameter>
  <Type>System.String</Type>
  <ParameterName>inputMessage</ParameterName>
 </Parameter>
 <Parameter>
  <Type>System.Int32</Type>
  <ParameterName>inputInt</ParameterName>
 </Parameter>
</Parameters>

為了方便起見我們在這裡定義一個FunctionScript的分布類來處理參數問題

namespace Phenix.DynamicCompiler
{
  public partial class FunctionScript
  {
    /// <summary>
    /// 返回參數列表
    /// </summary>
    /// <returns></returns>
    public List<CodeParameterDeclarationExpression> GetParameterList()
    {
      List<CodeParameterDeclarationExpression> parameterList = new List<CodeParameterDeclarationExpression>();
      if (this.Parameters!=null)
      {
        var x = from n in this.Parameters.Elements("Parameter")
            select n;
        foreach (var item in x)
        {
          parameterList.Add(new CodeParameterDeclarationExpression(
            Type.GetType((string)item.Element("Type")),
            (string)item.Element("ParameterName")));
  
        }
        
      }
     
      return parameterList;
    }
  }
}

然後我們需要一個代碼構造類,用於根據情況構造代碼:

namespace Phenix.DynamicCompiler
{
  public class CodeBuilder
  {
    StringCollection importNameSpace;
    List<FunctionScript> functionScriptList;
    string typeNamespace;
    string className;
  
    /// <summary>
    ///
    /// </summary>
    /// <param name="importNameSpace">導入命名空間</param>
    /// <param name="className">類名</param>
    /// <param name="functionInfoList">函數列表</param>
    public CodeBuilder(StringCollection importNameSpace,string typeNamespace,string className,
      List<FunctionScript> functionScriptList)
    {
      if (functionScriptList == null || functionScriptList.Count==0)
      {
        throw new Exception("函數列表不能為空");
      }
      this.importNameSpace = importNameSpace;

      this.typeNamespace = typeNamespace;
      this.className = className;
      this.functionScriptList = functionScriptList;
      if (functionScriptList.GroupBy(c => c.ClassName).Count() > 1)
      {
        throw new Exception("這些函數不屬於一個類");
      }
  
    }
    public CodeBuilder(string typeNamespace,string className,
      List<FunctionScript> functionScriptList):this( null,typeNamespace,className,functionScriptList)
    {
  
    }
    public CodeBuilder(List<FunctionScript> functionScriptList):
      this(functionScriptList.Count==0?null:functionScriptList[0].ClassNameSpace,
      functionScriptList.Count == 0 ? null : functionScriptList[0].ClassName,
      functionScriptList)
    {
    }
    private CodeCompileUnit CreateExecutionClass()
    {
      // 創建CodeCompileUnit以包含代碼
      CodeCompileUnit ccu = new CodeCompileUnit(); // 分配需要的命名空間
      CodeNamespace cns = new CodeNamespace(typeNamespace);
      cns.Imports.Add(new CodeNamespaceImport("System"));
      ccu.Namespaces.Add(cns); // 創建新的類聲明
      CodeTypeDeclaration codeClass = new CodeTypeDeclaration(className);
      cns.Types.Add(codeClass); // 在命名空間下加入類
      foreach (var functionScript in functionScriptList)
      {
        CodeMemberMethod method = new CodeMemberMethod();
        method.Name = functionScript.FunctionName;
        //方法的訪問標識符為public static
        method.Attributes = MemberAttributes.Static | MemberAttributes.Public;
        foreach (var parameter in functionScript.GetParameterList())
        {
          method.Parameters.Add(parameter);
        }
  
        if (functionScript.ReturnType != null)
        {
          method.ReturnType = new CodeTypeReference(Type.GetType(functionScript.ReturnType));
 
        }
        // 添加方法實體需要的代碼
        CodeSnippetStatement methodBody = new CodeSnippetStatement(functionScript.ScriptBody);
        method.Statements.Add(methodBody);
  
        codeClass.Members.Add(method);
  
      }
      return ccu;
    }
    public string GenerateCode()
    {
      // 調用我們前面的方法創建CodeCompileUnit
      CodeCompileUnit ccu = CreateExecutionClass();
      CSharpCodeProvider provider = new CSharpCodeProvider();
      CodeGeneratorOptions options = new CodeGeneratorOptions();
      options.BlankLinesBetweenMembers = false;
      options.BracingStyle = "C";//大括號換行
      options.IndentString = "\t";
      StringWriter sw = new StringWriter();
      try
      {
        provider.GenerateCodeFromCompileUnit(ccu, sw, options);
        sw.Flush();
      }
      finally
      {
        sw.Close();
      }
      return sw.GetStringBuilder().ToString();
    }
  }
}

下面我們再編寫一個用於編譯的類:

namespace Phenix.DynamicCompiler
{
  public class PhenixCompiler
  {
    /// <summary>
    /// 編譯
    /// </summary>
    /// <param name="codeString">需要編譯的代碼</param>
    /// <param name="outputAssembly">輸出程序集位置</param>
    public void Compile(string codeString, string outputAssembly)
    {
      CompilerParameters compilerParams = new CompilerParameters();
      ///編譯器選項設置
      compilerParams.CompilerOptions = "/target:library /optimize";
  
      compilerParams.OutputAssembly = outputAssembly;
      ///生成調試信息
      compilerParams.IncludeDebugInformation = false;
  
      ///添加相關的引用
      foreach (var item in ReferencedAssemblies)
      {
        compilerParams.ReferencedAssemblies.Add(item);
      }
  
      CSharpCodeProvider provider = new CSharpCodeProvider();
  
      ///編譯
      CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, codeString);
      if (results.Errors.HasErrors)
      {
        StringBuilder errors = new StringBuilder();
        foreach (CompilerError item in results.Errors)
        {
          errors.AppendLine(item.ToString());
        }
        throw new Exception(errors.ToString());
      }
      ///創建程序集
      Assembly asm = results.CompiledAssembly;
  
    }
    private StringCollection ReferencedAssemblies
    { get; set; }
    public PhenixCompiler(StringCollection referencedAssemblies)
    {
      ReferencedAssemblies = referencedAssemblies;
      ReferencedAssemblies.Add("mscorlib.dll");
      ReferencedAssemblies.Add("System.dll");
    }
    public PhenixCompiler()
      : this(new StringCollection())
    { }
  }
}

我們再構造一個用於執行的類:

namespace DynamicCompiler
{
  public class Executor
  {
    string inputAssembly;
    string instanceName;
    string methodName;
    public void Execute()
    {
      Assembly assembly = Assembly.LoadFrom(inputAssembly);
      MethodInfo mi;
      Type t = assembly.GetType(instanceName);
      object mode = assembly.CreateInstance(instanceName);
      mi = t.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public);
      mi.Invoke(mode, new object[0]);
    }
    public Executor(string inputAssembly,string instanceName,string methodName)
    {
      this.inputAssembly = inputAssembly;
      this.instanceName = instanceName;
      this.methodName = methodName;
    }
  }
}

主函數代碼如下:

主程序

class Program
{
const string functionIndentSpace = "\t\t\t";
static void Main(string[] args)
{
XElement f1parameter = new XElement("Parameters",
new XElement("Parameter",
new XElement("Type", "System.String"),
new XElement("ParameterName", "inputMessage")),
new XElement("Parameter",
new XElement("Type", "System.Int32"),
new XElement("ParameterName", "inputInt"))
);
FunctionScript f1 = new FunctionScript()
{
ClassName = "MyClass",
ClassNameSpace = "My",
FunctionName = "hello",
ReturnType="System.String",
Parameters=f1parameter,
ScriptBody = functionIndentSpace+"return \"hello\"+inputInt.ToString();"
};
FunctionScript f2 = new FunctionScript()
{
ClassName = "MyClass",
ClassNameSpace = "My",
FunctionName = "hello1",
ScriptBody = functionIndentSpace + "Console.WriteLine(hello(\"x\",1));"
};
List<FunctionScript> list=new List<FunctionScript>();
list.Add(f1);
list.Add(f2);
CodeBuilder c = new CodeBuilder(list);
Console.WriteLine(c.GenerateCode());
Compiler pc = new Compiler();
pc.Compile(c.GenerateCode(), "x.dll");
Executor ex = new Executor("x.dll", "My.MyClass", "hello1");
ex.Execute();

}
}

運行結果如下:

這裡邊工作的四個對象的時序圖如下:

源碼下載

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