.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]);?????//執行方法
????????????}
????????}
????}
}
?
了解了上面這段代碼,我們基本對動態編譯的概念清楚了,但是如果在項目中這樣使用的話,我們要自己去控制源代碼的全部文檔內容,如果大規模應用的話會非常的麻煩,需要重復編寫命名空間構造,類構造,函數構造等,如果我們還想看到一個格式良好的源碼,我們還必須自己控制格式?,F在我們來介紹一種源碼構造方式來解決這些問題,請看如下代碼:
?
動態編譯簡單代碼改進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();
????????}
????}
?
運行結果如下:
這里邊工作的四個對象的時序圖如下:
源碼下載
在項目中應用可以結合http://www.cnblogs.com/nuaalfm/archive/2008/09/08/1286640.html中的插件編程
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的.net项目的二次开发解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。