【转载】基于ASP.NET Web Application的插件实现,附DEMO
??? 使用插件機(jī)制來(lái)擴(kuò)展B/S程序,主要需要實(shí)現(xiàn)兩個(gè)功能,首先是動(dòng)態(tài)編譯插件中的C#代碼,然后利用反射來(lái)執(zhí)行插件已經(jīng)編譯的C#代碼。
???
??? 一、動(dòng)態(tài)編譯
??? 插件一般是以xml文件的形式實(shí)現(xiàn)其配置,必須要先讀取插件中的C#代碼才能進(jìn)行動(dòng)態(tài)編譯。
XmlDocument?doc?=?new?XmlDocument();?try?
{?
????//獲取插件配置文件?
????doc.Load(Server.MapPath(@"plugins/"?+?strPluginName?+?"/config.xml"));?
}?
catch?(Exception?e)?
{?
????Response.Write("插件載入錯(cuò)誤:"?+?e.ToString());?
????return;?
}?
XmlNode?xn?=?doc.DocumentElement.SelectSingleNode("csharpcode");?
string?code?=?string.Empty;?
if?(xn?!=?null)?
{?
????XmlElement?xe?=?(XmlElement)xn;?
????if?(xe.HasAttribute("link"))?
????{?
XmlDocument?doc?=?new?XmlDocument();?
try?
{?
????//獲取插件配置文件?
????doc.Load(Server.MapPath(@"plugins/"?+?strPluginName?+?"/config.xml"));?
}?
catch?(Exception?e)?
{?
????Response.Write("插件載入錯(cuò)誤:"?+?e.ToString());?
????return;?
}?
XmlNode?xn?=?doc.DocumentElement.SelectSingleNode("csharpcode");?
string?code?=?string.Empty;?
if?(xn?!=?null)?
{?
????XmlElement?xe?=?(XmlElement)xn;?
????if?(xe.HasAttribute("link"))?
????{?
using?(?
????????????StreamReader?sr?=?
????????????????System.IO.File.OpenText(?
????????????????????Path.Combine(?
????????????????????????Server.MapPath(@"plugins/"?+?strPluginName?+?"/"),?xe.GetAttribute("link")?
????????????????????)?
????????????????)?
?????????)?
????????{?
????????????code?=?sr.ReadToEnd();?
????????????sr.Close();?
????????}?
????}?
????else?
????{?
????????code?=?xe.InnerText;?
????}?
} 復(fù)制代碼
?
?.Net為我們提供了很強(qiáng)大的支持來(lái)實(shí)現(xiàn)這一切我們可以去做的基礎(chǔ),主要應(yīng)用的兩個(gè)命名空間是:System.CodeDom.Compiler和Microsoft.CSharp或Microsoft.VisualBasic。另外還需要用到反射來(lái)動(dòng)態(tài)執(zhí)行你的代碼。動(dòng)態(tài)編譯并執(zhí)行代碼的原理其實(shí)在于將提供的源代碼交予CSharpCodeProvider來(lái)執(zhí)行編譯(其實(shí)和CSC沒(méi)什么兩樣),如果沒(méi)有任何編譯錯(cuò)誤,生成的IL代碼會(huì)被編譯成DLL存放于于內(nèi)存并加載在某個(gè)應(yīng)用程序域(默認(rèn)為當(dāng)前)內(nèi)并通過(guò)反射的方式來(lái)調(diào)用其某個(gè)方法或者觸發(fā)某個(gè)事件等。之所以說(shuō)它是插件編寫(xiě)的一種方式也正是因?yàn)榕c此,我們可以通過(guò)預(yù)先定義好的借口來(lái)組織和擴(kuò)展我們的程序并將其交還給主程序去觸發(fā)。一個(gè)基本的動(dòng)態(tài)編譯并執(zhí)行代碼的步驟包括:
將要被編譯和執(zhí)行的代碼讀入并以字符串方式保存
聲明CSharpCodeProvider對(duì)象實(shí)例
調(diào)用CSharpCodeProvider實(shí)例的CompileAssemblyFromSource方法編譯
用反射生成被生成對(duì)象的實(shí)例(Assembly.CreateInstance
調(diào)用其方法
以下代碼片段包含了完整的編譯和執(zhí)行過(guò)程:
CodeSnippetCompileUnit?unit?=?new?CodeSnippetCompileUnit(code);?ICodeCompiler?compiler?=?new?CSharpCodeProvider().CreateCompiler();?
CompilerParameters?para?=?new?CompilerParameters();?
para.ReferencedAssemblies.Add("System.dll");?
para.ReferencedAssemblies.Add("System.Data.dll");?
para.ReferencedAssemblies.Add("System.XML.dll");?
para.ReferencedAssemblies.Add("System.Drawing.dll");?
para.ReferencedAssemblies.Add("System.Web.dll");?
para.ReferencedAssemblies.Add(Server.MapPath(this.Request.ApplicationPath?+?"/bin/PluginDemo.dll"));?
para.GenerateExecutable?=?false;?
para.OutputAssembly?=?dllPath;?
?
//編譯結(jié)果?
CompilerResults?cr?=?compiler.CompileAssemblyFromDom(para,?unit);?
if?(cr.Errors.Count?>?0)?
{?
????Response.Write("插件安裝失敗,編譯錯(cuò)誤:<br/>");?
????//遍歷編譯錯(cuò)誤?
????foreach?(System.CodeDom.Compiler.CompilerError?ce?in?cr.Errors)?
????{?
????????Response.Write(ce.ErrorText?+?"(文件:"?+?ce.FileName?+?",行號(hào):"?+?ce.Line.ToString()?+?",錯(cuò)誤編號(hào):"?+?ce.ErrorNumber?+?")<br/>");?
????}?
????return;?
} 復(fù)制代碼
???? 了解更多的關(guān)于動(dòng)態(tài)編譯的知識(shí)
這里引用了? System.CodeDom和System.CodeDom.Compiler。
讀取插件中的C#代碼并進(jìn)行動(dòng)態(tài)編譯后,要實(shí)現(xiàn)插件功能,就需要執(zhí)行已經(jīng)編譯好的插件的dll中的代碼了。
??? 二、利用反射實(shí)現(xiàn)插件功能
??? 反射的原理這里就不再說(shuō)了,三層架構(gòu)中用的太多了,呵呵。
public?string?GetPluginOutPut(string?strPluginName)?{?
????try?
????{?
????????//載入插件程序集?
????????System.Xml.XmlDocument?doc?=?new?System.Xml.XmlDocument();?
????????doc.Load(Server.MapPath("plugins/installedplugins.xml"));?
?
????????string?asmName?=?((System.Xml.XmlElement)doc.DocumentElement.SelectSingleNode("plugin[@name='"?+?strPluginName?+?"']")).GetAttribute("assembly");?
?
????????System.Reflection.Assembly?asm?=?System.Reflection.Assembly.LoadFrom(Server.MapPath(@"plugins/bin/"?+?asmName));?
????????IPlugin?plugin?=?(IPlugin)asm.CreateInstance("PluginDemo.plugins."?+?strPluginName);?
?
????????return?plugin.GetOutput(this);?
????}?
????catch?(Exception?ex)?
????{?
????????return?"插件‘"?+?strPluginName?+?"’載入失敗:"?+?ex.ToString();?
????}?
} 復(fù)制代碼
?
?忘了說(shuō)一下,要實(shí)現(xiàn)反射,我們需要先在網(wǎng)站中定義好的一個(gè)IPlugin接口
/**////?<summary>?///?插件接口?
///?</summary>?
public?interface?IPlugin?
{?
????/**////?<summary>?
????///?獲取插件輸出的內(nèi)容?
????///?</summary>?
????///?<param?name="page">System.Web.UI.Page</param>?
????///?<returns>字符串</returns>?
????string?GetOutput(Page?page);?
?
????/**////?<summary>?
????///?安裝插件時(shí)執(zhí)行的代碼?
????///?</summary>?
????///?<param?name="page">System.Web.UI.Page</param>?
????void?Install(Page?page);?
?
????/**////?<summary>?
????///?卸載插件時(shí)執(zhí)行的代碼?
????///?</summary>?
????///?<param?name="page">System.Web.UI.Page</param>?
????void?Uninstall(Page?page);?
?
????/**////?<summary>?
????///?更新插件時(shí)執(zhí)行的代碼?
????///?</summary>?
????///?<param?name="page">System.Web.UI.Page</param>?
????void?Update(Page?page);?
} 復(fù)制代碼
?
?
然后所有的插件的C#代碼都繼承自這一個(gè)接口。
還是把我做的DEMO發(fā)上來(lái)吧,講的不好,大家還是看源碼吧,里面都有注釋,呵呵。
下載地址:http://www.box.net/shared/a2ceqq67ts?
總結(jié)
以上是生活随笔為你收集整理的【转载】基于ASP.NET Web Application的插件实现,附DEMO的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: oracle-25031安装错误分析解决
- 下一篇: 深入浅出之Smarty模板引擎工作机制(