zoukankan      html  css  js  c++  java
  • AutoController通用自动增删查改

    我的春秋痴梦第三步:

    让所有的增删查改自动化,

                    不用每次都实例化一个controller然后郁闷的写增删查改的方法。

    效果:

    根据url 得到要操作的对象,自动对model属性过滤换转,完成CURD生成对应的回传给view。

    我的思路:

      @1 根据 提交的 url    /admin/article/create
      @2 实例化 article 对象   
      @3 根据 admin 和 用户找到  article 的限制
      @4 根据限制 过滤article 
      @5 再交给autoaction 即 直接 增改  
      @6 保存和上传(图片 文章 的话)
      @7 根据url对象返回页面

      @1 分析提交的 url    /admin/article/create   得到对象

      @2 实例化 (article) 对象     

      @3 根据 (article)对象 权限 找到  (article) 的限制(使用xml)  

      @4 根据限制 过滤(article)对象 

      @5 转换和上传(有图片之类的话)  

      @6 再交给auto controller即直接 增改   

      @7 根据url对象返回页面

     

    整个想法比较简单。和之前那篇一样让程序更通用更自动化。

    其实一开始接触MVC的时候就又这个想法了,当时没想好该怎么做增改这两块。

    前段时间看了老D分析Suteki.Shop 介绍了 里面一个自动脚手架

    两个周前我把他定下计划之中 I will make it!

    废话不多说。

    @1 可以想我一样自己分析url  也可以取得 route 里面的 controller的值

     

    按指定 格式 切割 字符串
    /// <summary>
    /// 按指定 格式 切割 字符串
    /// </summary>
    /// <param name="content"></param>
    /// <param name="sp"></param>
    /// <returns></returns>
    public static List<string> splitString(this string content, char sp)
    {
    if (string.IsNullOrEmpty(content))
    {
    return null;
    }
    List
    <string> zhan2 = new List<string>();
    string temp = "";
    foreach (var s in content)//content 的 格式是 /area/Article/show/1
    {
    if (s == sp)
    {
    zhan2.Add(temp);
    temp
    = "";
    }
    else
    {
    temp
    += s;
    }
    }
    zhan2.Add(temp);
    return zhan2;
    }

    当然 在这里 我并不推荐这样.直接取值更方便。

     @2 实例化(article) 对象并装配进去表单的值

    代码
    #region @2 实例化 article 对象 ===============================@2
    modelvalueDictionary
    = modelvalueDictionary.Init(req); // //装载字典
    if (modelvalueDictionary.Count == 0)
    { _errmsg
    = "没有发现任何值"; return modelname; }
    #endregion
    #region ========================================自动装配 所有
    foreach (var propertyInfo in modeltype.GetProperties())//赋值 遍历Article 属性
    {
    string val = null;
    Type convertType
    = propertyInfo.PropertyType;// 目标类型
    try
    { val
    = modelvalueDictionary[propertyInfo.Name.ToLower()]; }//目标值
    catch (Exception) { }
    if (!string.IsNullOrEmpty(val))
    {
    if (propertyInfo.PropertyType.IsNullable())//如果是 可空则 转换
    convertType = propertyInfo.PropertyType.ToNonNullable();
    var changedval
    = Convert.ChangeType(val, convertType);
    propertyInfo.SetValue(model, changedval,
    new object[0]);
    }
    }
    #endregion

    在上一篇中我提到了不知道如何转换可空类型后来发现了这篇文章 感谢乐于分享的牛人们。(感谢cnblogs)

    其中一个自己写的Init的扩展类似前一篇,就不瞎贴代码了。获得一个对象类似前一篇的根据string类型,

    得到类型都是静态工厂直接new一个出来。

     

     @3 根据(article)对象用户的权限 找到  (article) 的限制和转换(使用xml)  

        @3.1 我这里选择了xml没有使用加attribute的方式,为的是更改后不用重新编译。

     

    modelfilter
    <?xml version="1.0" encoding="utf-8" ?>
    <!--
    1 action up => type jpg
    2 function => par
    -->
    <models>
    <!--model name-->
    <model name="Article">
    <!--版本模型属性 1来自哪个 id (id由url转换) ,权限,版本什么的 -->
    <modelattr id="xxxx" role="everyone">
    <!--validata="int,>,5,过短,不能为空"验证信息: 1表达式 2出错信息可以 堆叠-->
    <FullTitle validata="int,>,5,过短" validata2="!=null,不能为空" class="myval1"/>
    <ShortTitle function="setvalue,1,不能赋值" class="myval1"/>
    <!--上传: 1上传后的路径 2格式 all ,type1 具体定制在 另一块 3出错信息-->
    <!--<IsAudit fileup="url1,jpg,上传失败" />-->
    <!--方法执行:方法全在filter下定义 1方法名2参数 :self 是指调用者自身3出错信息 -->
    <!--<ArticleType function="md5,self,无法加密" validata="notNull,不能为空"/>-->
    </modelattr>
    </model>
    <model name="Flash">
    <modelattr id="xxxx" role="everyone">
    <picUrl fileup="/UpFile/,*,上传失败" />
    </modelattr>
    </model>
    <coustum>
    <modelattr id="sss">
    <item class="myval1" validata="int,>,5,过短" validata2="!=null,不能为空" />
    </modelattr>
    </coustum>
    </models>

     这里我的想法比较多,但是很多都还没有实现。

     比如根据url生成验证的id版本,根据权限选择不同的url,类似html的css 可以自定义 验证和转换。

       @3.2 再来看看 Linq 2 XML 进行读取和解析 xml  

    Linq2XML
    var mp = HttpContext.Server.MapPath("~/modelFilter/modelFilter.xml");
    XMLHelper xmlHelper
    = new XMLHelper(mp);
    var xe5
    = xmlHelper.GetByModel(modelname, modeltype, rolegp);
    var fileups
    = xe5.Where(a => a.Attribute("fileup") != null);
    //<FullTitle validata="Length&gt;,5,过短" validata2="!=null,不能为空" />b
    foreach (var element in fileups)// <DownUrl fileup="/UpFile/,*,上传失败" />
    {

    我的xmlHelper

    XMLHelper
    public class XMLHelper
    {
    public XElement xElement;
    public XMLHelper(string path)
    {
    if (string.IsNullOrEmpty(path))
    throw new Exception("path is null ");
    xElement
    = XElement.Load(path);
    }

    public IEnumerable<XElement> GetByModel(string modelname, Type modeltype, string role)
    {
    var xe3
    = xElement.Elements().Where(a => a.Name == "model" && (a.Attribute("name") != null && a.Attribute("name").Value == modelname));
    //<modelattr id="xxxx" role="everyone" validata="notNull"> 根据 权限 和 版本 url 得到对应的 验证 信息
    var xe4 = xe3.Elements().Single(a => a.Name == "modelattr" && (a.Attribute("role") != null && a.Attribute("role").Value == role));
    var xe5
    = xe4.Elements().Where(a => modeltype.GetProperty(a.Name.LocalName) != null);
    return xe5;
    }
    }

    比较简陋 。。我是实用主义。

    这样我们就可以在

    foreach (var element in fileups){}// <DownUrl fileup="/UpFile/,*,上传失败" />  

    中间专心写自己的处理了。

     @3.3延迟执行 

        在这里的时候我,我突然发现如果在这里以检查上传没问题就即时上传的话,可能他并没有通过后面的检查

    而导致白白的消耗性能和硬盘。这里我先把要执行的方法先保存起来。(感谢重典兄的指点)

    private List<Func<bool>> functions = new List<Func<bool>>();

    @4 根据限制 过滤(article)对象  

    使用非常简单 依次添加,遍历执行即可。整个东西有3个需要做的,

    第一个是文件上传 

    第二个指定转换

    第三个验证,验证你也可以用其他的,比如分离自带的那个验证为自己用。

    文件上传
    #region 文件上传
    List
    <string> list = element.Attribute("fileup").Value.splitString(',');
    var httppostfile
    = req.Files.Get(element.Name.LocalName);//
    functions.Add(() =>
    {
    try
    {
    FileUp fileUp
    = new FileUp(httppostfile, this.Server.MapPath("~/UpFile/"));
    if (fileUp.errormessage != "")
    {
    _errmsg
    += fileUp.errormessage;
    return false;
    }
    fileUp.Up();
    //把属性装配上去
    var changedtype = modeltype.GetProperty(
    element.Name.LocalName).PropertyType;
    //1 得到属性 的值
    string re1 = fileUp.downpath;
    //2 转换为 nullable //3 赋值
    modeltype.GetProperty(
    element.Name.LocalName).SetValue(model, re1.toType(changedtype),
    new object[0]);
    return true;
    }
    catch (Exception)
    {

    _errmsg
    += list.LastOrDefault();

    }
    return false;

    });
    #endregion
    指定方法
    #region function
    var pif_filter
    = element.Attribute("function");//得到 属性
    List<string> list = pif_filter.Value.splitString(',');//切割 参数
    //延迟加载 方法
    functions.Add(() =>
    {
    try
    {
    //构造方法和参数
    Type type = typeof(Filter);
    Filter filter
    = new Filter();
    MethodInfo methodInfo
    = type.GetMethod(list[0]);
    var parameters
    = new object[1];
    parameters[
    0] = list[1];
    var re1
    = methodInfo.Invoke(filter, parameters).ToString();
    if (!string.IsNullOrEmpty(re1))
    {
    //赋值
    var changedtype = modeltype.GetProperty(
    element.Name.LocalName).PropertyType;
    modeltype.GetProperty(
    element.Name.LocalName).SetValue(model, re1.toType(changedtype),
    new object[0]);

    }
    return re1 != null;
    }
    catch (Exception e)
    {
    _errmsg
    += list.LastOrDefault();
    }
    return false;
    }
    );
    #endregion

    验证我这里就没有贴了比上面两者都简单 。

    指定方法的执行我采用的是调用我指定的一个对象下面的方法,本来 是想调用任意的,在xml那里写全对象的

    namespace那些就可以调用,后来还是放弃了,感觉不实用。方法也只是一个单参数的方法。比如

    md5加密和指定转换 普通用户发的文章为 未审核等。 

    @5 保存和上传(有图片之类的话) 

    var result = functions.All(a => a.Invoke());//都满足 返回 true

     @6 再交给auto controller即直接 增改  

    var ir = IRR.GetRepository(urlHelper.modelName.str2type());
    ir.InsertOnSubmit(model);
    ir.SubmitChanges();

    @7 根据url对象返回页面。这里给出我在controller中的实现:

    代码
    [HttpPost]
    [ValidateInput(
    false)]
    public ActionResult Create(object t)
    {
    var model
    = Created();
    #region //@5 再交给autoaction 即 直接 增改
    try
    {
    if (ModelState.IsValid)
    {
    if (_errmsg != "") return View(t);//验证
    Ir.InsertOnSubmit(model);
    Ir.SubmitChanges();
    return RedirectToAction("list"); //can't use strongly typed redirect here or the wrong controller name will be picked up
    }
    return View(model);
    }
    catch (Exception e)
    {
    Response.Write(e.ToString());
    return View(model);
    }
    #endregion
    }

    提醒一下

    1暂时那个    ModelState.IsValid 验证没有任何意义,因为传进来的model是object。

    2如果不知道我那个 public IRepository Ir;怎么出来的可以参见我的上一篇。

    在这里可以写成

    public class AutoController<T> : Controller where T : class, new()

    就可以使用验证了。我不这样做是因为 我不想每次都要实例化一个。来模板继承。

    但是目前我采用的做法仍然是 依靠一个 具体类 继承 我那个自动类。然后让原本访问具体类的增删查改方法的

    去访问自动类了。而这时我们只需要在xml里面写明规则那么一切OK。

    至此 问题依然 没有解决 那就是 由于 依托于具体类继承自动类。每次还是得实例化类。

    (我是后台:可不可以不要那么多XXXcontroller文件啊!)

    我们其实可以在 Global.asax 那里 拦截请求。

    不给controller执行 而是 构造自己的 RouteHandler 解析路由 和HttpHandler 转到view

     此处参考重典兄的 route 

     实现的思路

    1在 global 处 截断 指定格式的请求

    2构造自己的RouteHandler 进行解析

    3 分发给对应的 httphandler

    public static void RegisterRoutes(RouteCollection routes)
    {
    routes.Add(
    new Route("{page}.np", new MyRouteHandler()));//解析.aspx
    }

    我指定以.np后缀访问的都是我自动类处理的天下。

    我的想法是这样  /area(Default)/model/action/id.np 

    同时对应view页面的位置统一。其实现非常简单。参看重典兄的就可以做出来。

    这时我们会遇到几个问题

    1如何传值到页面 viewdata 肯定不行了,因为controller与我们渐行渐远。

    2此时view上的XXhelper ,HTML 的方法都没了,以前构造html的方法都没了。

    对于第一点 我们可以使用session 只需要加上IRequiresSessionState

    public class MyPage : IHttpHandler, IRequiresSessionState

    这里有个有趣的问题,但是我又不知道怎么回事。在构造函数执行的时候,session null不可用。

    而构造完后才开始有session。这是怎么回事呢。

    对于第二点我就有点无奈了。我参考源码,暂时还没把他整个方法移植过来。郁闷的是,由于那个是模板的,而我们

    已经没有了页面MODEL了。所以比较麻烦。很多方法里也内嵌了MODEL。即时勉强移植过来还是会存在一些不能用的

    语法但是又存在。感觉不爽。

    走到这里的时候我感觉到很吃力。 丢掉了controller之后,我还有一大堆view丢不掉,还有怎么和以前的路由兼容?

    不然的话整个view上路由的重写也有点小麻烦。

    感觉使用框架的便利,自己也被绑架到框架上了。自己的代码原始积累应该是与框架无关的啊!这样才能形成自己的

    代码库。

    最后由于无法丢弃以前的view暂时使用前一种方法,来优化自己的CMS。走到第三步。这次的感觉有点沉重。

    越发觉得自己的渺小和对知识认识的浅薄,当时以为委托 很简单,自己遇到了问题需要解决才发现,好多其实都

    没有理解好。另一点沉重也许是对未来的一种迷茫吧。基于框架的代码还是我们的代码吗?昨天有个牛人说他为了

    不让自己的代码有隐患,尽量不用第三方的框架SSH那些都不用。而是做自己的基于基础类库的原始积累。

    也许JAVA和.NET不一样吧。我始终坚持,一步一步优化,一步一步积累。把时间精力花在最需要解决问题的地方,

    慢慢的用自己的代码替换别人的框架,像我这次这样替换掉controller,也许有一天就用自己的东西替换了

    mvc了呢。虽然没有mvc的通用,但最合适自己。不是嘛?



    作者:撞破南墙
    出处:http://www.cnblogs.com/facingwaller/
    关于作者:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    PDA设备小知识--(IP)工业防护等级含义
    实施项目--如何推进项目实施进度
    Git.Framework 框架随手记--存储过程简化
    Git.Framework 框架随手记--SQL配置文件的使用
    Git.Framework 框架随手记--ORM查询数据集合 二
    介绍 Scratch 3.0:扩展编码创造力
    微服务架构:引领数字化转型的基石
    网易云技术开放日 云安全专场分享圆满结束
    JVM调优推荐
    springboot + mybatis + 多数据源
  • 原文地址:https://www.cnblogs.com/facingwaller/p/1742055.html
Copyright © 2011-2022 走看看