【自然框架】之表单控件(一)实体类(Class)VS 字典(Dictionary)

时间:2022-04-27
本文章向大家介绍【自然框架】之表单控件(一)实体类(Class)VS 字典(Dictionary),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

用一个具体一点的例子来说一下,我实现单表的添加、修改的思路和方式,顺便和三层里的实体类的方式做一下对比。

一、我的拆分思想之一

      简单的操作和复杂的操作分离开来,即简单的操作简单处理,复杂的操作其他方式处理。比如,单表的添加、修改操作,这个比较简单,没有什么复杂的业务逻辑,甚至可以说没有业务逻辑,那么这样的操作,我们就可以“提炼”出来单独处理,用一种简单的方法搞定。

二、适用范围

      这个要说明白了,否则会比较麻烦:)

      1、 信息管理类项目,就是使用关系型数据库保存数据的项目。比如网站的后台管理、OA、CMS、CRM、企业定制开发等。       2、 B/S方式。       3、 单表的添加、修改。(其实主从表的也可以使用,只是复杂了一点点,所以第一步先说简单的。)

      您可能会说,这个适用范围是不是太小了,没有什么意思。把范围限定小一点,是想控制一下,范围弄大了,就不好讨论了。

      这个只是第一步。简单的操作简单处理吗。

三、三层里面使用实体类来实现添加、修改数据的步骤。

      这里只说编码部分,不说调研、设计等部分。我们就以新闻信息为例,实现添加、修改新闻的功能。      

      1、 定义实体类。

public class News
    {
        private string newsTitle  ;      //新闻标题
        private string newsContent  ;    //新闻内容
        private DateTime addedTime;      //发布时间
        public string NewsTitle
        {
            get { return newsTitle; }
            set { newsTitle = value; }
        }
        public string NewsContent
        {
            get { return newsContent; }
            set { newsContent = value; }
        }
        public DateTime AddedTime
        {
            get { return addedTime; }
            set { addedTime = value; }
        }
    }

      2、 拖拽控件,绘制表单。       3、 从控件里面取值,然后给实体类赋值。

myNews.NewsTitle = txtTitle.Text;
myNews.NewsContent = txtContent.Text;
myNews.AddedTime = DateTime.Now;

      4、 处理SQL语句。

       这里有许多的实现方式,可以使用SQLHelp、微软的企业库、自己封装的类库、ORM、LinQ to SQL等,只是不管用什么方式,最终都是要得到一个SQL语句(包括参数化的SQL语句)。

      5、 提交给数据库。       6、 如果是修改的话,还有一个从实体类里面取值,给控件赋值的步骤。

      主要步骤就是这些,当然还有一些数据验证、逻辑处理等。由于单表的添加、修改比较简单,基本上没有什么业务逻辑,所以这个就暂且不提。注意:并不是说不用处理了。

问题:“冗余”代码过多。我们先看上面说的第三步,有一个字段就要写一行给实体类赋值的语句,如果一个项目有100个表,一个表里面有10个字段,那么就是1000个字段,至少1000行的语句,工作量不少嘛。而这些代码基本一致,除了控件名、类、属性不一样之外,都是一样的。这是必须要写,但是有没有什么“技术含量”的代码。当然了您可以使用代码生成器,可以找好几个人来分工。但是这只是治标不治本,并没有从根本上减少代码。

      那么如何从根本上减少代码呢?(可能您要问了,减少代码有什么好处呀?减少代码可以缩短时间,减少出错的机会,减少检查代码的工作量,减少修改代码的机会。)

四、我的思路

      ORM的思路是把类和表一级的做对应,类的属性和字段一级的作对应,而我的想法是把表、字段“提升一个级别”,即让“字段”和“类”作对应,“表”和“集合”做对应。而集合我选择了字典(Dictionary)。

      我们先定义一个类ColumnsInfoBase ,这个类要对字段进行描述。 

public class ColumnsInfoBase        
{
 #region 字段的基本信息的描述
 /// <summary>
 /// 配置信息里面的字段的标识
 /// </summary>
 public int ColumnID = 0;

 /// <summary>
 /// 数据库里的字段名称
 /// </summary>
 public string ColSysName = "";

 /// <summary>
 /// 显示给客户看的名称
 /// </summary>
 public string ColName = "";

 /// <summary>
 /// 字段类型,int nvarchar 等
 /// </summary>
 public string ColType = "";

 /// <summary>
 /// 字段大小
 /// </summary>
 public Int32 ColSize = 0;

 /// <summary>
 /// 字段值
 /// </summary>
 public string ColValue;

 #endregion

    }

      然后定义一个字典, 

Dictionary<int, ColumnsInfoBase> dic_BaseCols = new Dictionary<int, ColumnsInfoBase>();

      再然后,我们把的实例放到字典里面,这样我们的准备工作就做好了,下面看看如何实现从控件里面取值的操作。 

ColumnsInfoBase Title = new ColumnsInfoBase();
Title.ColSysName = "NewsTitle";
ColumnsInfoBase Content = new ColumnsInfoBase();
Content.ColSysName = "Content";
ColumnsInfoBase AddedTime = new ColumnsInfoBase();
AddedTime.ColSysName = "AddedTime";
dic_BaseCols.Add(1, Title);
dic_BaseCols.Add(2, Content);
dic_BaseCols.Add(3, AddedTime);

      有了字典就方便了,取值的时候我们来一个遍历就搞定了,

ColumnsInfoBase bInfo;
IControlMgr ControlValue = null;   //取值的接口

foreach (KeyValuePair<int, ColumnsInfoBase> info in dic_BaseCols)
{
    bInfo = (ColumnsInfoBase)info.Value;
 
    ControlValue = (IControlMgr)this.FindControl("c_" + bInfo.ColSysName);
    bInfo.ColValue = ControlValue.ControlValue;
}

      您可能要问了:这个ControlValue是什么呀,你用的是什么控件呀?.net自带的控件里面确实没有这个属性,而且郁闷的是不同的控件,取值的属性名称还都不一样(我并不是说这么做不对)。不一样怎么在遍历里面取值呀?自带的没有,那我们就定义一个接口让他有不就行了吗?(听说3.5里面可以使用扩展属性的方式了) 

      我定义一个接口,然后继承.net自带的控件,在实现这个接口就可以了。您说,这多麻烦呀,又是继承又是接口的,这要弄出来多少个控件呀?对于B/S来说,文本框、列表框、下拉列表框、复选框、复选组、单选组等,常用的就是这几个,把不常用的都加起来也不超过15个。常用的几个我已经都做好(点击下载)了,总共的代码才几十行(实现接口的代码)。相比我上面说的1000行代码可是少多了。而且这些控件,不仅可以在一个项目里面使用,其他的项目里面也可以使用,这样算起来节省的代码就更多了。

      再看上面说的第四步,其他的方式是怎么实现的我也不太清楚,我就不多说了,就说我的方法。我的方法就是写一个函数来处理SQL语句,可以拼接成SQL语句,也可以拼接成参数化的SQL语句(顺便处理一下储存过程的参数),还可以调用储存过程来处理。就是说这一步也可以只用一个函数就搞定,封装成DLL文件之后,各个项目就可以直接使用了。这样代码行数和字段数量就没有什么关系,即字段的增减并不会影响代码的行数。

      最后再来看看定义的部分。       您一定会说了,我的这个方法虽然在取值的时候用一个遍历就可以了,但是在定义的时候可是多写了好多行的代码呀? 其实我只是定义了一个类(ColumnsInfoBase),而 Title (ColumnsInfoBase Title)只是一个实例。定义类我是没有找到偷懒的方法,但是得到实例却是可以偷懒的。ColumnsInfoBase的属性类型都是字符串或者数字的,那么这些属性值就可以放在“配置信息”里面来存放,我可以做一个文件,把一个表单需要的字段的信息都放在这个文件里面,然后在用的时候读取出来,再做一个遍历就OK了。当然这回是遍历读取出来的配置信息,而不是遍历字典。

      最后的最后,我们还是看看UI吧,第二步我说了,要拖拽文本框这样的控件,但是我们一定要手动拖拽吗?我们可以用代码生成器嘛,当然我还是不喜欢这种方式,原因就是我不知道一个字段到底对应什么控件,对控件要如何“描述”(比如文本框的宽度、最大字符数等)。这些用代码生成器如何来生成呢?23号活动的时候我问了一下,没什么办法,只能手动修改了。这就带来了一个很大的问题:手动修改了代码生成器生成的代码后,如果有变动(比如增加了几个字段)了怎么办?我就不能直接使用代码生成器生成的“新的代码”覆盖以前的代码了,因为我手动修改了。不知道您对这样的问题是如何解决的,您是不是有更好的办法呢?

      我的方法就是做一个表单控件,让这个控件自己new控件(比如文本框)出来,那么一个字段到底要new出来什么控件呢?加说明,就是给字段增加 在表单里面 表现成什么控件的说明(其实是一个标识)。至于表单控件如何绘制页面,下次再说。

五、这种方法的优点:

      1、 增加字段、减少字段,可以不修改代码。增加字段就是往字典里面多加一个实例,减少一个字段就是少加一个实例,这样的话,还用修改代码吗?       2、 字段名变化了也不用修改代码。       3、 整理一下可以做成表单控件,这样就可以在多个项目里面通用了,节省更多的代码。试想,单表的添加、修改使用这样的表单控件来实现了,可以说不用写代码了,没有代码了还检查什么代码?修改什么代码?还怕代码风格不统一?(当然有一个前提,使用我的方法。我不是说一定要用,只是说我有这种方式。)       4、 可以使用一个页面实现多个表的添加、修改。比如新闻的添加、修改使用页面,产品信息(简单的)也可以使用页面。其他的单表的添加、修改也可以使用这个页面,这样又省下了不少页面。

六、缺点:       1、 适用范围比较小。       2、 如果想要找到一个字段,不是太直观。不适合业务逻辑复杂的情况。

Ps:       1、 ORM与UORM。       ORM就是类和表的对应,它是完全不管UI的,这样做就很灵活,可是适应更多的情况。灵活度高了,工作量也就多了,我们还要去处理UI。       而UORM就是把UI也划进来了。这样做灵活度降低、适用范围也小了,但是工作量却可以大大降低。 我是一个小小的coder,工作量大就意味着要加班加班再加班,而且没有加班费。郁闷呀,所以我要给自己减负,从根本上降低工作量。

      2、 权限控制到字段。       这要先向大家道歉了,本来想先写权限的,但是发现要想说好权限,还真不是一件容易的事情,要说明白我是如何“通用”的,就要先说依据的基础。恩,争取本周内结束。       假设我们要把权限的粒度做细,细到控制表单里的每一个字段,那么对于这种方式来说就很容易了,控制字段就相当于控制字典里的一个实例,有权限则添加这个实力,没有权限则不添加这个实力。 这是通用权限的一个基本思想,如果您没看懂也不要紧,过几天我会详细说明的。

3、 关于代码生成器。       其实代码生成器的原理和我的控件的原理是一样的,如果说代码生成器是事先编译的话,那么我的控件就是“运行时编译”。代码生成器要先生成代码,编译后才能使用。而我的表单控件呢,只要配置信息一遍,不用重新编译,立刻就能看到修改后的效果。这对于维护来说是很有优势的。

      我不喜欢代码生成器,不去使用代码生成器,才迫使我想出来了这样的方法,好与不好,这是一种尝试。如果我用代码生成器,“享受”代码生成器带来的好处,那我连尝试的想法都不会有。我怎么会找到更快捷的方式呢?