• 通用性站点管理后台(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的方式其实很简单。 关键是在实现《【讨论】一个接口的世界》的时候, 要注意性能。

  • 相关阅读:
    [原创] 为Visio添加公式编辑器工具栏按钮
    Matlab 图论最短路问题模型代码
    「SCOI2011」「LOJ #2441」 棘手的操作
    「APIO2012」「Luogu P1552」派遣
    「JLOI2015」「LOJ #2107」城池攻占
    「Wallace 笔记」LOJ 「数列分块入门」 9 题题解
    「Codeforces 235C」Cyclical Quest
    「Codeforces 1037H」Security
    「UVA 11468」Substring
    「LOJ #2102」「TJOI2015」弦论
  • 原文地址:https://www.cnblogs.com/hkf7703/p/2811589.html
Copyright © 2020-2023  润新知