项目架构说明

关于DTO

DTO(Data transfer object)数据传输对象是指在业务层,数据访问层,表示层各层间传递数据的数据结构的通称。在平时开发中用到的实体类,集合,DataSet,类型化数据集等等都是起到的DTO的作用。
DTO的实现方式一般可以分为“专门化”和“一般化”两个方向。对于“专门化”是指针对业务领域进行专门的建模设计,使用独有的类实现,例如一般情况专门编写的实体类。“一般化”指设计一个通用的存储对象,用来存储各种数据,例如微软的DataSet。它们各有其优缺点,不再赘述。(todo://)
但是在实际使用中有时会融合两者来做,例如微软的类型化数据集,LINQtoSQL它们的具体实现是基类使用通用存储对象来实现,子类再特化成专门的领域对象。还有一些兼顾两者优势的做法是通过工具和框架来实现各层数据的同步改动,例如Hibernate这样的Java框架。
在本项目中主要使用了“一般化”的方式来实现DTO,但是也有自身的特色。其主要体现在IDataElement接口中,该类描述了一个DTO必须可以取值(Value属性),可以遍历(实现了IEnumrateable接口),可以按照两种索引取值(参数为整数和字符串的索引器),可以关联父级数据项(Parent属性)。另外还有一个抽象类DataElement它作为IDataElement的默认实现存在,减少其他子类的代码量。
然后为DataElement编写了众多子类,不同的子类用来适配不同的真实数据类型。例如ObjectDataElement是用来把一般实体类对象适配成DataElement来使用;ArrayDataElement是用来把数组适配成DataElement来使用;DataTableDataElement用来适配DataTable对象;DataRowDataElement是用来适配DataRow对象;这样将不同的数据源适配到同一个接口一下。
另外在这里有四个特殊情况DataElement需要注意:
一. CustomDataElement 该类的设计作用是对别的DataElement的再次包装,这样通过重写其中的一些DataElement的功能,致使原来的DataElement产生特殊的数据存取变化,例如FooFun.Web.Controls项目中的ReplysViewControl.cs中的ReplysDataElement类,它实现了新的索引功能“OtherAnswers”“SelfAnswer”和“TopAnswer”。还有ForumDataElemnt对象,它提供了Parent属性的重新实现,对Parent的读取就是取板块的父板块。
二. EmptyDataElement该类起到了表示不存在的数据的作用。对于IDataElement的默认实现中,遍历、索引器取值当无法取值时都是不会出现错误的,而会返回一个EmptyDataElement的实例,它的IsEmpty属性始终是返回true的。
三. DTO是传输和保存数据的,数据在粒度上可以分为复合数据和原子数据(不可再细化的数据,如一个整数值、一个字符串),所以还存在这样的一批类IntDataElement,StringDataElement,它们在本项目的DTO框架中是写好的,用来适配原子数据。
四. //todo:还未完全实现 一个DataElement工厂,可以从键值对的结合中,即时构建出类来。主要用来可以从DataReader,Request.Params集合中构建出对象来。
这样各种数据类型的数据源就可以统一在IDataElement接口下,形成一个统一的访问方式。具体的演示可以参考ConsoleTest项目代码,该项目演示了XmlDataElement和DataTableDataElement是如何把不同数据存储形式,XML和DataTable适配到同一个IDataElement遍历取值框架之下的。

实现IDataElement的弊端及作用

使用IDataElement有一个比较严重的弊端是对其中的存储的数据类型的信息的严重忽视。所以在使用时都是使用Convert.To…系列方法来实现对数据的转型操作的,目前为止还没有发现严重问题。Convert.To…的实现在DataElement抽象父类中已经实现,大体是直接返回内部Value的值。
目前使用了IDataElement接口来适配数据以后,一个直接作用是用来和表示层做绑定操作。也就是表示层都是只针对IDataElement接口操作,针对IDataElement接口项目中实现了一系列模板控件,例如有EnumerableElementView 用来遍历数据项,ReversalTree用来反向追溯父元素(Parent属性),HierarchicalElement.cs文件中的TreeElement类向下遍历树。
从另外一个方面对数据库访问的层面在初步使用如下的设计方案,把数据库保存和查询问题归结为适配问题。
从DataReader中读取的数据直接可以通过PhysicalDataProvider项目中DataReaderEmit类,直接可以生成一个实体类,该实体类完全和DataReader中数据项是一致的。然后会用一个适配器类,该类的作用是将DataReader生成的类适配成OOP建模中的对象,例如PhysicalDataProvider项目中TopicService.cs中的TopicDataElement类,该类的作用是将数据库中查询出来的数据,适配成Topic实体,该实体由于是表示提问的问题,模型较为复杂,关系较多,所以使用的是XML节点来保存实体的。
而同时还有一个TopicDbDataElement类,该类的作用在于将XML复杂层次实体的模型建立为一个在表格中的数据项存储的实现。

皮肤系统

皮肤系统实现的关键在于SkinedControl类(自定义服务端控件),该类是TemplateControl的子类。关键是使用了TemplateControl的LoadControl(string ascxPath)方法,载入控件,然后插入子控件。
其中有两个属性决定了从哪个位置载入控件。Theme一般是不需要特别指定的,它会从默认设置,当前用户的设置,板块的设置等多方面获取该值。Skin是必须指定的,它决定了使用某主题下的具体页面。
其中的增强功能主要在两个方面:
一. 主题系统(Theme属性)。优先选择用户自己设置的主题,当然绝大部分板块应该是禁止用户设置和使用主题的。然后安装板块设置的主题选择主题,如果该级的版块没有设置主题,则取父板块的主题来使用
二. 并且当发现当前板块的主题皮肤不存在时,则使用默认的主题,以后考虑实现向父板块去主题皮肤看看皮肤是否存在。

Last edited Nov 2, 2009 at 7:32 AM by zhang3, version 4

Comments

No comments yet.