一个.net的系统的AOP设计思路一——NHibernate和界面/对象映射层

来源:互联网 发布:java开发web应用 编辑:程序博客网 时间:2024/06/05 18:24

     最近正在做一个项目,把其中的一些设计思路和大家共享,希望大家批评指正。

     业务背景:这是一个给某企业某部门开发的一个售前产品的报价系统。一些情况如下:
     1)虽然有签字的需求文档,但用户不能明确确定他们想要的,他们明确告诉我们的是,系统是一定要变的,小变一定,可能大变。
     2)系统的产品种类比较多,而且每种产品都有不同的信息,维护的方式有差异。
     3)用户希望把这个项目做成以后开发的模版.

    设计目标
    能够通过清晰的修改来应对变化。系统并不一定做到很灵活,但要做到修改程序的时候,思路明确,不会牵一发而动全身。我们认为用户的修改可以分为几个层次:a.界面变化;b.数据字段的增加、修改;c.局部业务逻辑发生变化;d.业务流程发生变化。对于这些不同层次的修改,可以有不同方法,但要保证一致性。

    设计思路:
     对于不同的变化采用不同策略:
     a.界面变化。解决方法:界面/对象映射层
     b. 数据字段的增加、修改。解决方法:NHibernate,界面/对象映射层
     c.局部业务逻辑发生变化。解决方法:页面控件校验映射,页面控件校验映射
     d.业务流程发生变化。本项目没有相关的设计,可能只有工作流可以解决这个问题了。

    具体方式:
    1)O/R maping采用NHibernate。这个大家很熟悉了,本文略。
    2)写一个界面/对象映射层。我们使用一个XML文件来表达如何把对象里的数据显示到界面。见下面的内容。这个配置文件并不处理控件的显示和位置,因为这是HTML的优势,系统只关心数据的绑定。

<?xml version="1.0" encoding="utf-8" ?>
<map>
<class caption="BaseCustomer" classname="SQS.BusinessRules.Data.BaseCustomer">
     <property controlid="txtProductGroupCode" buildclass="Map.PraseTextBox"  value="GroupCode" /> 
     <property controlid="txtProductGroupName" buildclass="Map.PraseTextBox" value="GroupName" 
           format="" />
  <property controlid="ddlProductTypeID" buildclass="Map.PraseDropDownList" 
           value="ProductTypeID" />
     <property controlid="cbStatus" buildclass="Map.PraseCheckBox" value="Status" />
     <property controlid="txtTarget2" buildclass="Map.PraseTextBox" subvalue="Target2" 
            value="TargetInformation" /> 
     <property controlid="txtTarget3" buildclass="Map.PraseTextBox" subvalue="Target3" 
            value="TargetInformation" />
</class>
</map>

     说明:
     class.caption:要绑定的对象关键字
     class.classname:对象的类型
     property .controlid:页面控件ID
     property .buildclass:控件绑定的解析器
     property .value:绑定的对象的属性名称
     property .format:比如对于日期你可以写成yyyy-MM-dd
     property .subvalue:绑定的对象的子属性名称(在后面有说明)

      以往的开发中,对象到界面往往需要花费大量代码,虽然可以用工具生成,但一些细节问题花费很大精力。比如:某表中一个字段的类型是日期,且某一行该字段值为null,(如无处理)到对象后将会变成默认值日期最小值,即时什么也没改保存的话,该行数据变成了这个最小日期。这样数据并处于不对称状态,以往在这方面,包含了大量的重复代码,采用这个结构以后,这种问题便不复存在。
       再举一个例子说明采用映射的好处,比如:如果防止SQL注入,有三种方式:a.写一套控件,b.在界面来判断,c.在数据库保证(比如用存储过程保存)。第一种方法比较花时间,而且如果采用了第三方的控件就不好办了。第二种方法,重复代码多,罗嗦。第三种方法效果比较好,但要求一定写存储过程或触发器,不太通用。
      另外呢,应对数据表字段的增加这种变化,我们写了这样一种映射(见下面)
   <property controlid="txtTarget2" buildclass="Map.PraseTextBox" subvalue="Target2" 
            value="TargetInformation" /> 
     <property controlid="txtTarget3" buildclass="Map.PraseTextBox" subvalue="Target3" 
            value="TargetInformation" />    <property controlid="txtTarget2" buildclass="Map.PraseTextBox" subvalue="Target2" 
            value="TargetInformation" /> 
     <property controlid="txtTarget3" buildclass="Map.PraseTextBox" subvalue="Target3" 
            value="TargetInformation" /> 

     大家不知是否注意到这两个映射中的value是相同的(都是TargetInformation),也就是说他们绑定到对象的同一个属性,那又何意义?请看在数据库中的存储的格式,大家就明白了:  


        
        大家可以看到,在数据库中,他们被存到了一个字段里,我们认为,如果用户增加的字段仅仅用于描述的话,这种方案还不错。或许您会说,这样在查询里面,如果想要查询某些字段是会很慢的,我门认为解决办法是:写一个触发器,在触发器中,分解字段(上面的TargetInformation),把相关的字段(比如Product3)存储到一个实际的字段(Product3)中去。这是一个混合的方法,面向对象和面向数据的结合。

原创粉丝点击