kevin fung's blog

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

  通常情况下,一个列表都由两部分组成,一部分是数据列表,另一部分则是数据查询。ArchOne中数据列表的实现方式在前几篇文章中已经描述的比较详细,本次我们主要对ArchOne中与列表关联的查询功能进行一个比较详细的讨论。

  公司之前的几套框架中对这部分都有比较成功的实现,核心原理都是拼SQL语句(LINQ表达式最终也是转换为SQL语句),这样的方案可以高效直接的实现查询功能并且查询条件之间的组合也比较容易实现。同时这个方案也有比较明显的三个缺点:

  1、扩展性不足:SQL语句或LINQ表达式直接在查询条件输入控件中以 Attribute 的形式出现,这要求所有实现查询的控件都必须从 WebControl 派生,这样一些从 Control 派生出来的自定义控件均无法使用;

  2、安全性较差:对 WebControl 增加的 Attribute 会直接输出到客户端代码中,这样将导致查询语句关联的字段及方式毫无保留的展现在所有人的面前,这给SQL注入式攻击提供了极大的便利性;

  3、体系架构混乱:在一个经过良好设计的项目中,拥有非常规范的体系架构,但由于框架的限制,必须在页面中进行拼写SQL语句而破坏了整个系统的设计,同时,由于数据持久层的差异(如使用 SQL Server 与 Oracle 而造成的 SQL 查询语句的差异)将会带来不可预测的缺陷,并且在更换持久层的情况下需要对所有查询功能重新进行调整。

  在 ArchOne 中,我们对此进行了重新设计,在开始设计之前,我们定下了以下两个目标:

  1、高扩展性及高安全性:支持任何从 Control 派生出来的控件作为查询条件输入控件,不破坏原有查询条件输入控件的任何结构(即不增加任何 Attribute);

  2、技术无关性:即无论你是使用 ADO.NET 还是 LINQ to SQL,亦或是使用 Entity Framework 对数据访问层进行持久化操作,但在 UI 层配置查询功能时均是完全一致的方式。

  为解决“高扩展性及高安全性”的目标,我们新开发了一个查询扩展控件 SearchExtender,通过使用该扩展控件对查询条件输入控件进行动态扩展而达到不破坏原有查询条件输入控件的任何结构的目标,也避免了造成查询语句的泄露而造成的安全性问题。

  要实现“技术无关性”的目标,我们定义了一个查询条件构造器接口(ISearchConditionBuilder),将真正的查询实现往后移(置入数据持久层)而在 UI 中不因架构的选择差异、数据持久层的选择差异而造成 UI 层的不一致,同时我们分别为不同的数据持久层及不同的技术体系实现了相应的查询条件构造器(如 SqlSearchConditionBuilder、OracleSearchConditionBuilder、LinqSearchConditionBuilder,这些构造器可在数据持久层中直接访问也可通过数据技术层中相应的基类中的查询条件构造函数间接访问)。

  下面我们通过简单的几行代码来看看查询功能是如何实现的。

 

查询条件输入控件
1 <table cellpadding="2" cellspacing="2" width="100%">
2 <tr>
3 <td>部门:</td>
4 <td>
5 <asp:DropDownList ID="ddlDepartment" runat="server" />
6 </td>
7 <td>姓名:</td>
8 <td>
9 <asp:TextBox ID="txtName" runat="server" />
10 </td>
11 <td>
12 <asp:LinkButton ID="lbtnSearch" runat="server"><span>搜索</span></asp:LinkButton>
13 <asp:LinkButton ID="lbtnReset" runat="server"><span>重置</span></asp:LinkButton>
14 </td>
15 </tr>
16  </table>

  通过上面的代码我们可以看到,这与普通的表单没有任何区别,下面我们看看如何使用 SearchExtender 控件来实现对查询条件输入控件的扩展:

 

前端 HTML 查询扩展控件
1 <Framework:SearchExtender ID="searchExtender" runat="server">
2 <ConditionItems>
3 <Framework:ConditionItem ControlId="ddlDepartment" ControlProperty="SelectedValue" FieldName="DeptID" OperatorType="Equal" ValueType="String" />
4 <Framework:ConditionItem ControlId="txtName" ControlProperty="Text" FieldName="Name" OperatorType="Like" ValueType="String" />
5 </ConditionItems>
6 <ConditionGroups>
7 <Framework:ConditionGroup RelationType="And">
8 <Framework:ConditionGroup ConditionName="DeptID" />
9 <Framework:ConditionGroup ConditionName="Name" />
10 </Framework:ConditionGroup>
11 </ConditionGroups>
12  </Framework:SearchExtender>

  最后,我们的查询有两种表现方式,一种是查询需要复用,这样的情况下,我们一般会将查询封装成一个 UserControl,因此,这个 UserControl 需要从 SearchBaseControl 派生;另外一种是不需要复用的,我们可以通过将 SearchExtender 控件置入 SearchContainer 容器来实现。如下:

  

1 <Framework:SearchContainer ID="searchContainer" runat="server">
2 <Framework:SearchExtender ID="searchExtender" runat="server" />
3  </Framework:SearchContainer>

 

posted on 2010-08-02 10:43  kevin fung  阅读(260)  评论(0编辑  收藏  举报