通用性站點管理后台(Bee OPOA Platform) (3)- MVC特性


接上篇(通用性站點管理后台(Bee OPOA Platform) (2)- 快速開發特性)

當時在系統構建的一開始就想引入MVC特性, 本人比較偏向於這種方式, 對Asp.net 基於事件這種方式不是特別興趣。 對純粹的http的調用方式很喜歡(可以用Fiddler攔截, 以便查找原因), 以最基本的Get/Post方式及請求的參數列表, 則可以很清晰的知道該請求與服務器端對應的關系。

配置

該特性開發目前基於以下配置文件展開的(IIS 7跟這個有點不一樣, 具體可以參看Codeplex項目中的web.config文件):

<add verb="*" path="*/*.bee,*.*.bee" type="Bee.Web.AuthMvcDispatcher, Bee.Security" validate="true" />

Bee.Web.AuthMvcDispatcher類型集成了RBAC權限中的權限判斷, 會在前端發起請求的時候統一判斷該請求是否有權限, 若沒有, 則返回一個Json結構的結果。該類型繼承與Bee.Web.MvcDispatcherBee.Web.MvcDispatcher是繼承接口有IHttpHandler,IRequiresSessionState,該Handler將處理所有后綴為bee的請求。

Action執行

目前只支持相當於Asp.net MVC框架中的最簡單的路由規則, 即/{ControllerName}/{ActionName}.bee的方式。 獲取了ControllerName, 就要獲得對應的Controller類型。

如何獲取ControllerName對應的Controller類型呢? 這個往往是先收集程序集中所有可能的Controller類型, 即掃描所有程序集放到一個對應的Dictionary集合中, 如:

            foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
                {
                    string s = a.FullName.Split(',')[0];
                    if (!s.StartsWith("System.") && CouldBeControllerAssemebly(s))
                    {
                         foreach (var type in assembly.GetTypes())
                        {
                            if (!type.IsInterface && !type.IsAbstract && type.IsSubclassOf(CbType)
                                  && !type.IsGenericType
                                  && type.Name.EndsWith("Controller"))
                            {
                                  // 符合就放入對應的ControllerName與類型的集合
                                 }
                        }
                    }
                }

有心的人很容易看出來這里有個問題, 什么問題呢? 就是不同NameSpace相同ControllerName的問題。 這個的問題解決與否, 其實取決於系統的應用, 若不是設計像微軟這樣做基礎性工作的設計的話, 完全可以不需要考慮該情形。 您認同嗎? 本平台對該類情形未作考慮。對應關系找出來后, 生成一個Controller實例就無問題了。 現在問題轉化到:給你一個類, 根據摻入的方法名及參數, 請動態調用盡可能合適的方式。

回頭看《【討論】一個接口的世界》, 就相當於要實現方法 object Invoke(object entity, string methodName, BeeDataAdapter dataAdapter);

那實現該方法的關鍵在於哪里呢?本人的解決方案的是通過Emit動態生成對應的代理類。 下面將結合實例(該實例可通過http://beeopoa.codeplex.com獲得, 通過svn方式可獲得源碼)。

我建立如下的Controller:

public class MVCTestController : ControllerBase
    {
        public int Add(int i, int j)
        {
            return i + j;
        }

        public int Add(int i)
        {
            return i;
        }

        public int Add(int i, BeeDataAdapter dataAdapter)
        {
            return 100;
        }
    }

對應生成的ControllerProxy的代碼(該代碼由於是動態產生, 可通過Reflector查看\Bee.OPOADemo\Cache\Bee_Core_EntityProxy\BeeCoreEntityProxy.dll獲的。 該dll為程序運行時產生, 在整個應用程序關閉時, 會生成該dll, 以便調試及查看用。)

public class MVCTestControllerProxy : EntityProxy<MVCTestController>
{
    // Methods
    public override object GetPropertyValue(object obj1, string text1)
    {
        MVCTestController controller = (MVCTestController) obj1;
        if (text1 == "ControllerName")
        {
            return controller.ControllerName;
        }
        return null;
    }

    public override object Invoke(object obj1, string text1, BeeDataAdapter adapter1)
    {
        MVCTestController controller = (MVCTestController) obj1;
    // 該方法為具體的匹配方法, 該方法已被混淆
    A2MkIq94Rgjx rgjx = ReflectionUtil.ABGkd(typeof(MVCTestController), text1, adapter1);
        string str = rgjx.AAkp5A9X;
        BeeDataAdapter aBGkd = rgjx.ABGkd;
        switch (str)
        {
            case "Int32 Add(Int32, Bee.BeeDataAdapter)":
                return controller.Add((int) ConvertUtil.Convert(aBGkd["i"], typeof(int)), (BeeDataAdapter) ConvertUtil.Convert(aBGkd["dataAdapter"], typeof(BeeDataAdapter)));

            case "Int32 Add(Int32, Int32)":
                return controller.Add((int) ConvertUtil.Convert(aBGkd["i"], typeof(int)), (int) ConvertUtil.Convert(aBGkd["j"], typeof(int)));

            case "Int32 Add(Int32)":
                return controller.Add((int) ConvertUtil.Convert(aBGkd["i"], typeof(int)));
        }
        return null;
    }

    public override void SetPropertyValue(object obj1, string text1, object obj2)
    {
        MVCTestController controller = (MVCTestController) obj1;
        text1 = text1.ToLower();
        if ((obj2 != null) && (obj2 != DBNull.Value))
        {
        }
    }
}

該類的方法GetPropertyValue及SetPropertyValue是針對設計ORM時或者在序列化反序列化的場合下有用, 該篇將講解Invoke方法。

由於方法名可能相同, 但參數不同的情形。 及c#中關鍵詞out及ref的引入, 對該問題的處理有點繁雜, 我們只關注最需要關注的那部分, 首先在設計之初就不考慮對out及ref的支持。

那么問題基本就回歸到參數匹配的問題, 思路如下: 1. 參數匹配以名稱匹配為准, 簡單類型為主;2.以參數多的開始匹配;3. 復雜類型這參數列表中最多只能有一個。

若能匹配則調用對應的方法, 執行。

Action執行返回值的處理

平台提供了一個ActionResult的基類, 並且提供了擴展類ContentResult(直接發送內容, 如直接的文本), JsonResult(實例的Json格式), PageResult(視圖), RedirectResult(頁面跳轉)

StreamResult(流類型,文本, 或者圖片)。

並且在Controller的基類ControllerBase提供了內置方法。 如Json, View。  基本上都還比較好理解, 轉化一下加入Response的流中輸出。 如何執行對應的PageResult呢? 如何使用不同的視圖引擎來渲染呢? 說白了, 其中的過程就相當於一段文本(View, aspx文件, cshtml文件以及其他), 然后有很多值(Model, 及ViewData 或者諸如其他), 然后得到最終輸出的結果。 呵呵, 很像基於模板的代碼生成器, 。  最經典的莫過於Asp.Net WebForm引擎, 本平台目前也只實現基於此的視圖引擎, Razor方式, 及NVelocity, 暫未實現。 因為該方式最簡單及Framework中內置的:

object o = System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(object));

該方法直接創建基於路徑的page實例, 其中的緩存及路徑查找你都不用考慮。 若要實現自己的引擎則需要考慮這些。

總結

c#中實現MVC的方式其實很簡單。 關鍵是在實現《【討論】一個接口的世界》的時候, 要注意性能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM